Compare commits

..

310 Commits

Author SHA1 Message Date
Marco Costalba
78e6b361c5 Stockfish 2.2.1
Hopefully fixed the "lose on time" issue.

stockfish bench signature is: 5457475

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 17:01:40 +01:00
Marco Costalba
aa392c366e Extra time management safety
Further increase safety against time losses. After this
change (tested on LittleBlitzer and cutechess) I had no
more time losses at 2" and 1"+0.02 TC both on Windows
and Linux on more than 10000 games.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 13:24:37 +01:00
Marco Costalba
f80c50bcdd Try hard not to lose on time
We try hard not to lose on time even under extreme
time pressure. We achieve this through 3 different but
coordinated steps:

    1) Increase max frequency of timer events

    2) Quickly return after a stop signal

    3) Take in account timer resolution

With these SF played under LittleBlitzer at 1"+0.02 and 3"+0
without losing on time even one game.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 01:41:45 +01:00
Marco Costalba
d282cf6964 Avoid a race at thread creation
Before creating main thread we set its do_sleep flag to true,
then thread is created and it will go to sleep in main_loop()
after resetting do_sleep.

But if after the setting of do_sleep and before its resetting
the UI thread calls start_thinking() it will not wait on:

  if (!asyncMode)
      while (!main.do_sleep)
          cond_wait(&sleepCond, &main.sleepLock);

as it should but will immediately return before the main thread has
started the search. This very rare race show itself during bench,
when the first position is erroneusly skipped so that bench node count
results of 5309038 instead of the correct 5457475.

The patch is somewhat tricky, but is simple and it works!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 21:31:50 +01:00
Marco Costalba
b1fcfe4c5d Streamline generation of MV_NON_EVASION
Small speed-up of 3% in perft.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 19:18:38 +01:00
Marco Costalba
30418a3cfc Fix a warning under gcc
Locals left and right shadow two same named
variables in the std::ifstream base class.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 19:01:47 +01:00
Marco Costalba
cb1709ef5e Revert cond_signal() fix
It seems it yields to missing wake-up events with the
result of SF loosing on time as reported by many people.

So revert the patch and use a more robust approach: assume
there can be spurious wake ups events and make the code to
work also in those cases.

While debugging I found that WaitForSingleObject() had wrong
parameter 0 instead of INFINITE yielding to a crash while
exiting under Windows, strangely unnoticed til now.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-02 16:44:04 +01:00
Marco Costalba
67338e6f32 Big renaming of move's helpers
The aim is to have shorter names without losing
readibility but, if possible, increasing it.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-02 12:03:54 +01:00
Marco Costalba
8300ab149c Simplify Book APIs
Retire open(), close() and name() from public visibility
and greately simplify the code. It is amazing how much
can be squeezed out of an already mature code !

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-01 14:46:18 +01:00
Marco Costalba
c00443b19e Restore development version
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 15:44:00 +01:00
Marco Costalba
9db9e4f7d3 Stockfish 2.2
stockfish bench signature is: 5457475

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 15:43:28 +01:00
Marco Costalba
22e40c8c10 Fix cond_signal() semantics when using OLD_LOCKS
In Windows when OLD_LOCKS is defined we use SetEvent() to mimic
the semantic of the POSIX pthread_cond_signal().

Unfortunatly there is not a direct mapping because with SetEvent()
the state of an event object remains signaled until it is set
explicitly to the nonsignaled state or until a single waiting thread
has been released. Instead in case of pthread_cond_signal(), if there
are no waiting threads it has no effect. What we may want is something
like PulseEvent() instead of SetEvent(). Unfortunatly it is documented
by Mcrosoft as 'unreliable' due to spurious wakes up that could
filter out the signal resetting. So we opt to reset manually any
pending signaled state before to go to sleep.

This fixes the strange misbehaves during 'stockfish bench'
when using OLD_LOCKS under Windows.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 15:40:12 +01:00
Marco Costalba
e9296d694c Unify BitCountType selection
Now that HasPopCnt is a compile time constant we can
centralize and unify the BitCountType selection.

Also rename count_1s() in the more standard popcount()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 10:46:14 +01:00
Marco Costalba
fb4b4f772e Fix Windows 64 build
Broken by previous patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 08:48:16 +01:00
Marco Costalba
f4dadee5e2 Reformat types.h
Retire obsolete code and reshuffle stuff.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 19:13:44 +01:00
Marco Costalba
808a312e1c Simplify debug functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 16:23:11 +01:00
Marco Costalba
93e539d909 Assorted cleanups in benchmark.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 15:23:33 +01:00
Marco Costalba
9d7a36121a Retire RootMove::nodes
Was used for time management but is no more used
today.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 14:16:15 +01:00
Marco Costalba
8307da0de7 Update copyright year to 2012
And refresh Readme.txt while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 13:52:16 +01:00
Marco Costalba
ad43ce1436 Simplify printing of engine info
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 13:19:20 +01:00
Marco Costalba
0a6532a39d Retire run-time detection of hardware POPCNT
It was meant to build a single binary optimized
for any kind of CPU: with and without hardware POPCNT.

This is a nice idea but in practice was never used, or
people builds binary with popcnt enabled or not, mainly
according to their type of CPU. And it was also never
used in the official Jim's builds where, in case, would
be easier for a number of reasons, do build two different
versions: with and without SEE42 support.

So retire this feature and simplify the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 12:17:03 +01:00
Marco Costalba
20a6f99cdb Fix an off-by-one bug in ucioption.cpp
Harmless but anyhow wrong.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 11:58:55 +01:00
Marco Costalba
4554d8b2ac Better use STL algorithms in Endgame functions
This leads to a further and unexpected simplification
of this already very size optimized code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-30 11:58:54 +01:00
Marco Costalba
9cb187762a Wait for main thread to finish before to exit
Currently after a 'quit' command UI thread raises stop
signal, exits from uci_loop() and calls Threads.exit()
while the search threads are still active.

In Threads.exit() main thread is asked to terminate, but
if it is parked in idle_loop() it will exit and free its
resources (in particular the shared Movepicker object) while
sibling slaves are still active and this leads to a crash.

The fix is to let the UI thread always wait for main thread
to finish the search before to return from uci_loop().

Found by Valgrind when running with 8 threads.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-29 10:33:06 +01:00
Marco Costalba
4a8c1b2470 Use for_each() in Endgames d'tor
And fix some comments while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-29 10:25:11 +01:00
Marco Costalba
0759d8f430 Add user-defined conversions to UCIOption
Greatly improves the usage. User defined conversions
are a novelity for SF, another amazing C++ facility
at work !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-28 19:42:50 +01:00
Marco Costalba
ae65ab25d5 Fix score_to_uci()
The condition for a mate score was wrong:

abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY

instead of

abs(v) < VALUE_MATE_IN_PLY_MAX

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-28 14:20:24 +01:00
Marco Costalba
24417a6cd9 Better document how mate scores are stored in TT
During the search we score a mate as "plies to mate
from the root" to compare in an homogeneous way the
values returned by different sub-trees. However we
store in TT a mate score as "plies to mate from the
current position" the let the TT value remain valid
across the game.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-28 13:33:31 +01:00
Marco Costalba
ad4739a6d4 Retire SquaresByColorBB[] and enum SquareColor
Use same_color_squares() instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-28 10:57:08 +01:00
Marco Costalba
a695ed65a8 Rename Pieces
Align with PieceType naming convention and
make them more readable.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-28 10:38:48 +01:00
Marco Costalba
750ac9ac50 Document mate distance pruning
It is simple but somewhat tricky code that deserves
a bit of documentation. A bit of renaming while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-27 18:53:00 +01:00
Marco Costalba
07f3f0384a Assert enhancements in search
Add the check that alpha < beta - 1 if and only if PvNode is true.
The current code would not flag PvNode and alpha == beta - 1. In
other words, the || is not an exclusive OR!.

Also sync assert conditions of search() and qsearch()

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-27 01:04:00 +01:00
Marco Costalba
87b483f999 Reformat UCI option code
Make a better use of C++ operators overloading to
streamline the APIs.

Also sync polyglot.ini file while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-27 00:56:11 +01:00
Marco Costalba
c2d42ea833 Rename getters functions removing 'get_' prefix
Follow the suggested Qt style:

http://doc.qt.nokia.com/qq/qq13-apis.html

It seems to me simpler and easier to read.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-25 11:50:59 +01:00
Marco Costalba
b5f6c2241b Restore std::cout instead of printf()
I am not able to reproduce the speed regression anymore,
and also we were using cout even before speed regression
so probably the reason is not there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-25 09:29:51 +01:00
Marco Costalba
e89a8e0913 Correctly define operators in types.h
Be consistent with the way these operators are defined
in plain C (and in C++).

Spotted by Lucas Braesch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-24 09:58:46 +01:00
Marco Costalba
d543a64cc7 Don't update killers for evasions
We don't use killers to order evasions, so it
seems natural do not consider an evasion cut-off
move as a possible killer. Test shows almost no
change, as it should be becuase this is a really
tiny change, but neverthless seems the correct
thing to do.

After 11893 games
Mod vs Orig 1773 - 1696 - 8424 ELO +2 (+-3.4)

Idea from Critter.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-24 09:25:20 +01:00
Marco Costalba
939b621e5c Use ADL to skip std:: qualifier
Take advantage of argument-dependent lookup (ADL) to
avoid specifying std:: qualifier in some STL functions.
When a function argument refers to a namespace (in this
case std) then the compiler will search the unqualified
function in that namespace too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-18 21:18:51 +01:00
Marco Costalba
a77a3b723f Disable again buffering at startup
Partially revert efd2167998
Without this patch SF does not send "bestmove" to GUI.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-17 16:59:32 +01:00
Marco Costalba
976270916b Headers cleanup in ucioption.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-17 15:58:19 +01:00
Marco Costalba
87f7e99bc4 Use printf() instead of std::cout()
Seems sensibly faster: On a

./stockfish bench > /dev/null

We have +2% on mingw and even +5% on MSVC !

Also removed the nice but complex enum set960 machinery,
use directly the underlying move_to_uci() function.

Speed regression reported by Heinz van Saanen.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-17 15:50:06 +01:00
Marco Costalba
72d8d27234 Retire update_history() Inline the only caller site
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-16 19:17:47 +01:00
Marco Costalba
1ae6ae9b60 Fix book move with searchmoves compatibility
Do not return the book move if is not among the
RootMoves,in particular if we have been asked to
search on a move subset with "searchmoves" then
return book move only if it is among this subset.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-16 07:23:24 +01:00
Marco Costalba
af4fadebda Simplify id_loop() signature
And related assorted cleanup of this
very important function.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-14 21:56:37 +01:00
Marco Costalba
0af3af5d25 Reformat sending of PV information
Introduce pv_info_to_log() and pv_info_to_uci() and
greatly cleanup this stuff.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-14 19:22:22 +01:00
Marco Costalba
852d45a600 Further simplify aspiration code
Actually after last patch it happens that delta
starts always with the fixed value of 16.

So further remove useless code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-14 06:18:33 +01:00
Marco Costalba
96213689e4 Simplify aspiration window calculation
It seems that we just need to look at previous score to
compute aspiration window size.

After 5350 games:
Mod vs Orig 800 - 803  - 3647 ELO +0 (+- 5.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-14 05:56:53 +01:00
Marco Costalba
4e59c5c274 Retire RootMoveList
Diretcly use the underlying std::vector<Move> and the
STL algorithms. Also a bit of cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-12 20:13:24 +01:00
Marco Costalba
7d97ebfe7f Fix another crash triggered by previous patch
It is ok to redirect st pointer to startState, but the latter
should be updated with the content pointed by the st of the
original position. The bug is hidden when startState and *st
are the same as is the case of searching from start position,
but as soon as moves are made (as is the case when splitting)
the bug leads to a crash.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-11 11:44:09 +01:00
Marco Costalba
377c406c74 Fix a crash when quitting while searching
The Position object used by UI thread is a local variable in
uci_loop(), so after receiving "quit" command the function
returns and the position is freed from the stack.

This should not be a problem becuase in start_thinking() we copy
the position to RootPosition that is the one used by main search
thread. Unfortunatly we blindly copy also StateInfo pointer that
still points to the startState struct inside UI position. So the
pointer becomes stale as soon as UI thread leaves uci_loop() and
because this happens while main search thread is still recovering
after the 'stop' signal we have a crash.

The fix is to update the pointer to the correct startState after
the copy.

Found with Valgrind.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-11 10:31:46 +01:00
Marco Costalba
91601d7f95 Prune silly comments in search()
Comments should be informative but not pedantic / obvious.
The only exception is the function description where we
indulge a bit on the "chatty" side, but has always been like
this since Glaurung times, so we continue with this tradition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-10 19:14:49 +01:00
Marco Costalba
f3728e66f8 Allow to prune also first move
Tested togheter with previous patch; shows no regression and
is a semplification.

After 5817 games:
Mod vs Orig 939 - 892 - 3986 ELO +2 (+- 5.1)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-10 17:35:37 +01:00
Marco Costalba
3f14ed6602 Don't update bestValue when pruning
Simply return a fail-low score

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-10 17:35:36 +01:00
Marco Costalba
14df99130f Fix description of endgame scaling functions
Triggered by a comment of Eelco on talkchess. Also
a bit of cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-10 12:40:05 +01:00
Marco Costalba
47bcb892af Fix compile for 64 bits
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-08 13:55:27 +01:00
Marco Costalba
6e05055f06 Set captureThreshold according to static evaluation
Consider negative captures as good if
still enough to reach beta.

After 7502 games:
Mod vs Orig 1225 - 1158 - 5119 ELO +3 (+- 4.5)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-08 13:48:18 +01:00
Marco Costalba
da6e53a436 Remove some (int) casts
A cast rarely is the right solution. In this case was enough
to redifine 3 variables with type size_t instead of int

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-07 17:30:28 +01:00
Marco Costalba
56774fff20 Retire all extensions (but checks) for non-PV nodes
It seems we don't have any added value. Note that the
moves that were used to be extended are still flagged
as dangerous so to avoid at least pruning them.

After 9555 games
Mod vs Orig 1562 - 1540 - 6453 ELO +0 (+- 4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-07 07:49:24 +01:00
Marco Costalba
3527a42063 Prefer empty() to size()
As Heinz says:

"Function empty() should have a constant run-time even
 on lousy compilers and you spare the not.

The change is even measurable: + 100-150 nodes/sec. Wow:-)"

Patch from Heinz van Saanen

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-06 19:41:48 +01:00
Marco Costalba
98352a5e84 Use operator() instead of apply() in endgames
It is more idiomatic for a functor (a function object) as are
the endgames.

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-05 21:04:32 +01:00
Marco Costalba
11a7980976 Fix disambiguation bug in move_to_san()
A pinned piece cannot move and so does not play any role
in SAN disambiguation.

Reported by Steven Edwards.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-04 12:24:15 +01:00
Marco Costalba
5b8ca1eee7 Move SearchStack under Search namespace
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-04 12:03:59 +01:00
Marco Costalba
81cd417b45 Retire move.h
Also some assorted comments fixes and other trivia.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-04 11:36:03 +01:00
Marco Costalba
a44c5cf4f7 Prefer 0 to EmptyBoardBB
Easier and even faster or at least easier to optimize.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-03 12:02:13 +01:00
Marco Costalba
5c5af4fa65 Retire neighboring_files_bb() overload
Rarely used and we prefer to not hide the complexity.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-03 11:58:55 +01:00
Marco Costalba
efd2167998 Don't disable IO buffering at startup
It was never clear to me why we needed this trick, and now
that we rely only on C++ std::getline() and std::cout for
input / output it is even more a mistery what this code does.

So disable it and wait to see if someone screams ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-03 10:54:44 +01:00
Marco Costalba
348f824104 Tidy up comments in uci.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-03 10:48:31 +01:00
Marco Costalba
0f7cbaca75 Tidy up comments in thread.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-03 09:58:46 +01:00
Marco Costalba
e870afa5d5 Include <cstring> in search.h
Now we use memset() directly there.

Spotted by Justin Blanchard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-28 07:30:31 +01:00
Marco Costalba
9bacd921fa Little reformat of elapsed_search_time()
Change name and argument type.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-27 22:36:13 +01:00
Marco Costalba
bb3427ca85 Detach search arguments from UI thread
Detach from the UI thread the input arguments used by
the search threads so that the UI thread is able to receive
and process any command sent by the GUI while other threads
keep searching.

With this patch there is no more need to block the UI
thread after a "stop", so it is a more reliable and
robust solution than the previous patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-27 17:46:18 +01:00
Marco Costalba
6809b57cfc After a "stop" do not read new input until search finishes
Unfortunatly xboard sends immediately the new position to
search after sending "stop" when we have a ponder miss.

Becuase main thread position is not copied but is referenced
directly from root position and the latter is modified by
the "position.." UCI command we end up with the working position
that changes under our feet while the search is still recovering
after the "stop" and this causes a crash.

This happens only with the (broken) xboard, native UCI does not
have this problem.

Reported by otello1984

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-27 12:19:33 +01:00
Marco Costalba
ffa75215cc Fix a race in pondering mode
Fixes an hang when playing with ponder ON. Perhaps there is still
a very small race but now it seems engine does not hang anymore.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-26 18:29:40 +01:00
Marco Costalba
c4517c013c Introduce Search namespace
Move global search-related variables under "Search" namespace.

As a side effect we can move uci_async_command() and
wait_for_stop_or_ponderhit() away from search.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-26 13:43:22 +01:00
Marco Costalba
ed04c010eb Rewrite async I/O
Use the starting thread to wait for GUI input and instead use
the other threads to search. The consequence is that now think()
is alwasy started on a differnt thread than the caller that
returns immediately waiting for input. This reformat greatly
simplifies the code and is more in line with the common way
to implement this feature.

As a side effect now we don't need anymore Makefile tricks
with sleep() to allow profile builds.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-26 11:51:12 +01:00
Marco Costalba
e9dc2e9e1e Reformat search dispatch code
Reduce indentation level and lines of code and tidy up
some comment.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-20 00:02:13 +01:00
Marco Costalba
9c7d72739c Fix regression with printing of debug info
Output of debug info each second was disabled
due to recent patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-19 14:37:57 +01:00
Marco Costalba
3fc08f8ab6 Don't check for early stop when StopOnPonderhit is set
If we are pondering we will stop the search only when
GUI sends "ponderhit" or "stop" commands or when we reach
maximum depth. In all the other cases we continue to search
so there is no need to verify for available time.

Also better clarify why wait_for_stop_or_ponderhit() before
to exit in some cases.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-19 12:04:42 +01:00
Marco Costalba
c56a7ee803 Early stop: retire redundant Rml.size() == 1 case
In case there is only 1 legal move we will stop the
search at depth 10 anyway because the exclusion search
probe will fail low.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-19 11:19:12 +01:00
Marco Costalba
35fa4fc71f Rewrite early stop logic
In the "easy move" paradigm we verify if the best move has
ever changed over a good number of iterations and if the
biggest part of the searched nodes are consumed on it.
This is a kind of hacky ad indirect heuristic to deduce
if one move is much better than others.

Rewrite the early stop condition to verify directly if one
move is much better than others performing an exclusion
search.

Idea to use exclusion search for time management if form Critter.

After 12245 games at 30"+0.1
Mod vs Orig 1776 - 1775 - 8694 ELO +0 (+-3.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-18 21:01:34 +01:00
Marco Costalba
b56a098cfb CLOP: Passed pawns weights tuning
Tuned with CLOP against a pool of 3 engines. Result
verified with a direct match:

After 11720 games at 10"+0.1
Mod vs Orig 1922 - 1832 - 7966 ELO +2 (+-3.6)

So no change in self match but if CLOP is right it should
be a bit better against an engine pool.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-14 09:05:20 +01:00
Marco Costalba
4cc272cb94 Rename value in bestValue in id_loop()
The value returned by root search it is actually
our best value, so rename the variable to reflect this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-13 10:54:30 +01:00
Marco Costalba
c4fc82c6b7 Rewrite link time optimization in Makefile
Instead of binding link time optimization to the choice of
popcount support, do the right thing and add -flto option
when gcc 4.5 or later is detected.

Although it should be supported also under mingw, it happens
that it doesn't, at least on my 4.6.1 due to some known bugs.

Thanks to Mike for helping me with this patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-12 11:55:45 +01:00
Marco Costalba
a40ded2884 Simplify passed pawns logic
Remove the bonus for no *friendly* pieces in the pawn's path and
reduce a bit the bonus based on kings proximity.

This patch is part of to the ongoing effort to remove form evaluation
all the terms that do not add value.

After 16284 games:

Mod vs Orig 2728 - 2651 - 10911 ELO +1 (+- 3.1)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-12 09:09:41 +01:00
Marco Costalba
44c78fdb7a Fix regression: engine hangs while pondering
After a "stop" due to a ponder miss Xboard sends
immediately the new position to search, without
waiting for engine to effectively stop the search.

It is not clear if this is a GUI bug (as I suspect)
or allowed behaviour, but because it won't be fixed
anyway workaround this issue making listener thread
to switch to in-sync mode as soon as a "stop" command
is received.

Thanks to Mike Whiteley for reporting this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-10 15:08:53 +01:00
Marco Costalba
ecb98a3330 Stop is not an unknown command
If GUI sends stop while we are waiting for
a command do not reply with a silly:

Unknown command: stop

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-10 15:06:28 +01:00
Marco Costalba
9b6b510ca8 Another fix to profile-build on gcc 4.6
Oliver reports profile builds error with new gcc 4.6, he says:

"We need to add -lgov with profile-generate AND profile-use.
So it has to be added to the second stage of building too.

The problem occurred first with the introduction of gcc4.6 and
I think this is because the previous version did find the gcov
library automatically. gcc4.6 needs more precise options and
does less guesses. I have seen it in debian, Ubuntu and also with
mingw on Windows. And all use gcc4.6."

This patch fixes the issue.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-09 05:20:35 +01:00
Marco Costalba
c2600a73cf Fix profile-build
After async I/O patches 'bench' changed behaviour and now waits for
input at the end of the test run. This is due to listener thread stay
blocked on std::getline() even after test run is finished, as soon as
we feed something the thread unblocks and then quickly exits.

This is not a big problem, but has the bad side effect of breaking
profile builds that hang forever at the end of the test run.

The tricky workaround is to create a pipe that connects to stockfish
input and then, when test run is finished, breaking the pipe: this
makes std::getline() immediately return.

So this patch adds a 'sleep 10' piped into 'stockfish bench' test run
command. After 10 seconds sleep ends, the pipe breaks and 'bench'
finishes as usual.

Thanks to Oliver Korff for reporting the issue, and to Mike Whiteley
for having co-authored this solution.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-08 07:39:06 +01:00
Marco Costalba
43204d9ac2 Reformat all_slaves_finished()
Rename and move under ThreadsManager class.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-06 13:45:47 +01:00
Marco Costalba
369789b426 Better document and reshuffle stuff in think()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-06 13:32:25 +01:00
Marco Costalba
8fa53a5b92 Better define wait_for_stop_or_ponderhit()
Use do_uci_async_cmd() instead of process input commands
directly and clarify that what we are waiting for is
something that is able to raise StopRequest flag.

Also fix some stale comments in do_uci_async_cmd(). Here
we need to reset Limits.ponder only upon receiving "ponderhit".
In the case of "quit" or "stop" resetting Limits.ponder has no
effect because the search is going to be stopped anyway.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-06 12:48:16 +01:00
Marco Costalba
d58176bfea Use a timer to avoid polling
The timer will be fired asynchronously to handle
time management flags, while other threads are
searching.

This implementation uses a thread waiting on a
timed condition variable instead of real timers.
This approach allow to reduce platform dependant
code to a minimum and also is the most portable given
that timers libraries are very different among platforms
and also the best ones are not compatible with olds
Windows.

Also retire the now unused polling code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-05 18:19:38 +01:00
Marco Costalba
0095f423f2 Retire now unused input_available()
With our new listener thread we don't need anymore
this ugly and platform dependent code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-05 08:43:44 +01:00
Marco Costalba
2617aa415e Rewrite how commands from GUI are read
Instead of polling for input use a dedicated listener
thread to read commands from the GUI independently
from other threads.

To do this properly we have to delegate to the listener
all the reading from the GUI: while searching but also
while waiting for a command, like in std::getline().

So we have two possible behaviours: in-sync mode, in which
the thread mimics std::getline() and the caller blocks until
something is read from GUI, and async mode where the listener
continuously reads and processes GUI commands while other
threads are searching.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-05 08:35:17 +01:00
Marco Costalba
22b9307aba Further touches to magic bitboards code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-11-01 10:38:01 +01:00
Marco Costalba
ac7339877b Fix compile error in cpu_count()
The std::min() template function requires both arguments
to be of the same type.

But here we have the integer MAX_THREADS compared to a long:

long sysconf(int name);

So cast to integer and fix the compile.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-31 20:03:30 +01:00
Marco Costalba
90890844ad Document magics bitboards code
Add comments and rename stuff to better clarify what the
magic bitboard initialization code does.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-31 18:57:13 +01:00
mcostalba
8a89b12641 Merge pull request #1 from Panthee/master
Code Cleanup - Replacing macros Min() and Max() with corresponding STL algorithms std::min() and std::max()
2011-10-31 05:32:18 -07:00
Alexander Kure
4a3b162c8c Retire update_gains() 2011-10-31 03:28:59 -04:00
Alexander Kure
5c8af7ccb8 Replaced macros Min() and Max() with corresponding STL algorithms std::min() and std::max() 2011-10-31 00:38:44 -04:00
Marco Costalba
7942e6f3bf Retire update_gains()
Called from one place only.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 23:05:31 +01:00
Marco Costalba
c6f497f09d Fix small bug in move_attacks_square()
We test if the piece moved in 'to' attacks the square 's' with:

bit_is_set(attacks_from(piece, to), s))

But we should instead consider the new occupancy, changed after
the piece is moved, and so test with:

bit_is_set(attacks_from(piece, to, occ), s))

Otherwise we can miss some cases, for instance a queen in b1 that
moves in c1 is not detected to attack a1 while instead she does.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 19:31:50 +01:00
Marco Costalba
29be28e1a2 Inline pinned_pieces() and discovered_check_candidates()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 18:48:23 +01:00
Marco Costalba
e7939f450f Code style and 80 chars cols in Position::from_fen()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 18:02:18 +01:00
Marco Costalba
81801d395f Sync do_move() and undo_move()
It is not possible to unify due to the fact that the
sequence steps are reversed. What we can do is to try
to sync comments and code as much as we can to easy
reading and documentation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 13:58:44 +01:00
Marco Costalba
bc76c62c63 Explicitly use a dedicated bitboard for occupied squares
Instead of byTypeBB[0]. This better self-documents the code.

No functional and speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 13:51:20 +01:00
Marco Costalba
fd5d6c5340 Retire do_capture_move()
It is called only in do_move() that now has been fully
expanded. This is the most time consuming function of
all the engine.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-30 10:59:55 +01:00
Marco Costalba
08abe8b4a3 Retire undo_null_move()
Use a templetized do_null_move() to do/undo the null move.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-29 18:01:56 +01:00
Marco Costalba
e896368496 Retire undo_castle_move()
Use a templetized do_castle_move() to do/undo the castling.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-29 17:31:15 +01:00
Marco Costalba
2fe4e10b0b Retire Position::set_castling_rights()
Is called in just one place. And rename set_castle() in the
now free to use and more appropiate set_castle_right().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-29 13:43:44 +01:00
Marco Costalba
f2e78d9f84 Retire PieceValueXXX[] getters
They don't add any value given that the corresponding
table has global visibility anyhow.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-22 16:06:59 +01:00
Marco Costalba
b5bbc1f713 Simplify the promotion case of move_gives_check()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-22 15:01:21 +01:00
Marco Costalba
23943208ec Remove redundancy in definitions of attack helpers
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-22 14:25:53 +01:00
Marco Costalba
c555e1aa96 Convert PST tables to relative values
This is a prerequisite to allow changing piece values
at runtime, needed for tuning.

Also use scores instead of separated midgame and endgame values.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-22 13:17:24 +01:00
Marco Costalba
36cc214703 Increase Mobility
First tuning with CLOP against a pool of 3 engines. Result
verified with a direct match:

After 8736 games at 10"+0.1
Mod vs Orig 1470 - 1496 - 5770 ELO -1 (+-4.3)

So no change in self match but if CLOP is right it should
be a bit better against an engine pool.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-20 07:06:05 +01:00
Marco Costalba
8f7d36a85d Better document mate and stalemate detection
In particular add that we can have an harmless false positive
in case StopRequest or thread.cutoff_occurred() are set.

Reported by David Lee.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-18 20:01:21 +01:00
Marco Costalba
e7cfe42d3f Use newly added log facility instead of LogFile
As a side effect now log file is open and closed every
time it is used instead of remaining open for the whole
thinking time.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-17 21:07:14 +01:00
Marco Costalba
500fff920b Add basic log facility
Mainly used to log stuff to a file while playing, when
stdout is used for the comunication with the GUI.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-17 21:07:00 +01:00
Marco Costalba
782c3f36cc Fix compile error in debug mode
Build broken by commit 3141490374
where we renamed move_is_ok() in is_ok() and this clashes
with the same named method in Position that overrides the
move's one causing compile errors.

The fix is to rename the method in Position.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-16 23:56:25 +01:00
Marco Costalba
8272dcb6cd Link Time Optimization doesn't needs -static
Justin reports that it breaks the compilation on Fedore 15 and as Tom says:

-static is only needed to work around the gcc on ubuntu 11.10 beta bug.
If -static introduces issues on its own then it is better to remove it.
It will not be needed in most environments.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-09 08:24:56 +01:00
Marco Costalba
fec623d68d Better document how MultiPV search works
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-08 12:11:53 +01:00
Marco Costalba
a3bf09c5c9 Send again all the PV lines in multiPV searching
Partially revert 1036cadcec because UCI protocol
in case of multipv explicitly requires:

for the best move/pv add "multipv 1" in the string when you send the pv.
in k-best mode always send all k variants in k strings together.

Thanks to Justin Blanchard for pointing this out.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-08 10:24:54 +01:00
Marco Costalba
325dedc7da Added gcc -msse3 support
It is enabled when selecting x86-64-modern target, this gives
another nice speed up:

On a Core i5-2500 (3300 Mhz, Sandy Bridge):

64 bit download version: 1597151 n/s

-flto : 1659664 n/s

-flto -msse3: 1732344 n/s

Patch suggested by Tom Vijlbrief.

Also unify flto, popcount and msse3 optimization under "modern"
target, note that this can break the "modern" build on old gcc that
do not support -flto option: in this case update gcc ;-) or default
to the standard build.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-08 08:50:11 +01:00
Tom Vijlbrief
09b5949cd2 Added gcc lto (Link Time Optimization) option
Just by adding the -flto option to CXXFLAGS link command
we can gain a few percent in speed.

On a Core i5-2500 (3300 Mhz, Sandy Bridge):

64 bit download version:

Without -flto: 1597151 n/s
With -flto : 1659664 n/s

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-06 20:10:12 +01:00
Marco Costalba
3141490374 Shrink names of move helpers
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-03 14:18:57 +01:00
Marco Costalba
1a8e3f0b2e Small touches in position.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-03 14:18:56 +01:00
Marco Costalba
80dd90f972 Small touches to book.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-03 14:18:30 +01:00
Marco Costalba
c2c185423b Better naming borrowed from Critter
In line with http://chessprogramming.wikispaces.com conventions.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-02 10:16:59 +01:00
Marco Costalba
25b4d0c127 Revert "Retire Rml full PV search at depth == 1"
Yet another random crash source !

Hopefully this is the last one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-02 07:40:46 +01:00
Marco Costalba
2225e3bbe7 Rename kingZone[] and reverse the king's color
Seems easier to understand to me. From Critter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-02 06:54:57 +01:00
Marco Costalba
a90a990118 Document why Book is defined static
This was not clear to someone on talkchess and actually
is not trivial to understand.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-02 06:54:57 +01:00
Marco Costalba
83d8fe2d59 When exiting wake up all threads at once
It seems we have a very rare crash under Linux, once
every 10K games without this patch.

Is faster to wake up all the threads, especially on SMP,
where the threads can then exit in parallel while the main
thread is waiting for the next one to terminate.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-10-02 06:54:43 +01:00
Marco Costalba
63a04134d0 No need to test for MOVE_NONE before move_is_ok()
Function move_is_ok() already catches the move == MOVE_NONE case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-25 16:30:29 +01:00
Marco Costalba
6bc16f3ff1 Update killers after a TT hit
Almost no increase but seems the logic thing to do.

After 16707 games 2771 - 2595 - 11341 ELO +3 (+- 3.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-24 17:20:35 +01:00
Marco Costalba
10b24af98a Correctly score capture underpromotions
Be sure a queen capture promotion is tried in front of
an underpromotion.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-24 17:13:14 +01:00
Marco Costalba
be9aba2fa0 Don't lock before check for termination
Restore old locking scheme changed with
commit 1e92df6b20.

This seems to prevent a very rare crash that occurs
once every 5-10K games.

With this patch we have no crashes after 33K games.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-24 17:12:36 +01:00
Marco Costalba
35018fa307 Use the map type template parameter to access map()
It is more natural than using the family subtype and also
use two single maps instead of a std::pair.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-11 11:38:23 +01:00
Marco Costalba
b706165527 Lookup square distance instead of calculate on the fly
Microptimization that gives a +0.7% speed increase.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-09-11 10:11:43 +01:00
Marco Costalba
6963c3802d Detect family type of endgame from its enum value
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-22 00:49:47 +01:00
Marco Costalba
3b67636f0e Indulge a bit on the template wizardy
Push the template pedal a bit in our "showoff" endgame code ;-)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-14 13:15:58 +01:00
Marco Costalba
48e39c5c8e Small simplification of endgame functions API
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-14 12:12:14 +01:00
Marco Costalba
13524bea9b Fix use of uninitialized variable
When initializing endgames map we build a faked FEN string
in mat_key() to get the position hash's key.

This fen string lacks full move numbers, so when parsing the
fen in Position::from_fen() we leave startPosPly un-initialized.

Spotted by Valgrind (this is a kind of bug that is almost impossible
for humans to find).

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-12 11:56:11 +01:00
Marco Costalba
500c7f44ab Fix silly icc remark #2259
Another stupid remark to quiet out:

remark #2259: non-pointer conversion from "int" to "UINT16={unsigned short}"
may lose significant bits

In this case icc always converts to an integer the result of a shift operation
if the bit size of the operand is smaller, hence the warning when assignin
back to n.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-12 11:54:54 +01:00
Marco Costalba
d156e7a20b Use a boolean instead as thread's state
Now that we have just two mutually exclusive thread's states
we can repleace them by a simple boolean.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-10 12:26:52 +01:00
Marco Costalba
c386ce0023 Remove Thread::WORKISWAITING
Set the state directly to Thread::SEARCHING

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-10 12:05:33 +01:00
Marco Costalba
b69d9ee3f7 Don't need pthread_detach() after pthread_join()
Spotted by Joona and verified with Valgrind.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-10 08:17:15 +01:00
Marco Costalba
7d5b8fcf77 Change start_routine argument
Directly pass the thread pointer.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-09 14:19:44 +01:00
Marco Costalba
bc6a6e04a0 Retire Rml full PV search at depth == 1
Now that Rml ordering is based on normal MovePicker logic,
apart for the ttMove that is given, we can avoid to score
all the root moves at depth 1. We only need it for easy move
detection logic, but in this case we just need to score the
first two best moves and not all the Rml set.

No regression after 6400 games
Mod vs Orig 1052 1012 4336 ELO +2 (+- 4.9)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-09 09:58:10 +01:00
Marco Costalba
e5ffe9959c Retire ThreadsManager::init_hash_tables()
Allocation of pawn and material hash tables should
be strictly bounded to the change of the number of
activeThreads, so move the code inside set_size().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 23:08:34 +01:00
Marco Costalba
86b95f2105 Retire Thread::TERMINATED
Use proper way to detect for thread terimnation instead of
our homegrown flag.

It adds more code than it removes and adds also platform specific
code, neverthless I think is the way to go becuase Thread::TERMINATED
flag is intrinsecly racy given that when we raise it thread is still
_not_ terminated nor it can be, and also we don't want to reinvent
the (broken) wheel of thread termination detection when there is
already available the proper one.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 22:10:01 +01:00
Marco Costalba
1e92df6b20 Retire Thread::INITIALIZING
Was used to prevent issues when creating multiple threads
on Windows, but now it seems we can remove it safely.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 14:12:02 +01:00
Marco Costalba
ba85c59d96 Move idle_loop() under Thread
This greatly removes clutter from the difficult idle_loop() function

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 13:23:03 +01:00
Marco Costalba
dafd5b5864 Tidy up comments in thread.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 11:49:58 +01:00
Marco Costalba
eabba1119f Retire broken SendSearchedNodes
Now that we can split at root it happens that SendSearchedNodes
works only once at the end of the iteration, but this is useless
becuase speed info is sent anyhow toghter with the pv line.

So retire for now, waiting to find something SMP compatible.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 10:08:41 +01:00
Marco Costalba
06e0d48794 Sync search() and qsearch() alpha update
Change qsearch() to reflect alpha update logic
of search().

To be consistent changed also moves loop condition and
futility pruning condition.

No regression after 5072 games at TC 10"+0.1

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 07:46:01 +01:00
Marco Costalba
4a71c86270 Retire Thread::BOOKED
Start a slave as soon as is allocated.

No functional change with faked split.

Regression tested the full split() series and after
2000 games no regression and no crash.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-08 07:45:24 +01:00
Marco Costalba
68583f4d48 Fix obey the "maxThreadsPerSplitPoint" setting
Spotted by Joona.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 15:15:19 +01:00
Marco Costalba
5b35c149e8 Do not modify alpha in split()
When calling split or we immediately return because unable to
find available slaves, or we start searching on _all_ the moves
of the node or until a cut-off occurs, so that when returning
from split we immediately leave the moves loop.

Because of this we don't need to change alpha inside split() and
we can use a signature similar to search() so to better clarify
that split() is actually a search on the remaining node's moves.

No functional change with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 15:10:41 +01:00
Marco Costalba
9a542d9698 Initialize a new split point out of lock
Allocate and initialize a new split point
out of lock becuase modified data is local to
master thread only.

Also better document why we need a lock at
the end of split().

No functional change with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 14:37:14 +01:00
Marco Costalba
cd27ed9a74 Update comment on why we call root search with ss+1
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 10:36:14 +01:00
Marco Costalba
b9f8cb7837 Fix an assert when stopping the search
When StopRequest is raised we cannot immediately exit the
move loop but first we need to update bestValue so to avoid
assert:

assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 10:36:13 +01:00
Joona Kiiski
ffa150bec3 Split at root!
Another great success by Joona !

After 5876 games at 10"+0.1
Mod vs Orig: 1073 - 849 - 3954 ELO +13 (+- 5.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-07 10:35:56 +01:00
Joona Kiiski
13bc6ba2c6 Preparations for splitting at root
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-06 10:31:30 +01:00
Marco Costalba
30b3edf328 Simplify MovePickerExt<>
Now that we don't special case the root moves anymore
we don't need to pass NodeType anymore as template parameter,
a simple bool to detect a SpNode will be enough.

Spotted by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-05 07:23:54 +01:00
Marco Costalba
7902d6089e Fix silly bug in uci loop
After issuing "go"-command, at the end of the search
SF shows: "Unknown command: ...".

Spotted by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-05 07:15:45 +01:00
Marco Costalba
b6b0878da8 Fix a (silly) warning under icc compiler
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-04 10:10:03 +01:00
Marco Costalba
3a76163aba Use std::lexicographical_compare() in UCI options
Instead of our home grown function to perform a case
insensitive compare on option names as required by UCI
protocol.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-03 15:00:23 +01:00
Marco Costalba
1e7c6fc761 Consistently set ttMove to Rml.pv[0] in root node
No functional change, but reduce risks of subtle aliasing bugs.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 22:28:03 +01:00
Marco Costalba
c50ad85c3c Fix an off-by-one error in UCI print loop
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 21:47:31 +01:00
Marco Costalba
bb6a6e159a Rename ok_to_use_TT() in can_return_tt()
Seems more appropiate. From Lucas Braesch's DoubleCheck.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 18:49:06 +01:00
Marco Costalba
1036cadcec Send PV only for updated lines
It seems FritzGUI already remembers the old lines, so
we just need to update PV info only for the new lines.

Also introduced prevScore field in RootMove to avoid
a bulk copy of Rml.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 18:00:47 +01:00
Marco Costalba
c71ae794df Sort PV moves always in two steps
This should fix following issue:

Suppose the search with MultiPVIteration == 0 returns an exact score

move = Nxf4, score = 100

Now search with MultiPVIteration == 1 and get two scores

move = Qg8, score = 150
move = Ra1, score = 180

If we now reorder all the moves in one step we end up with

pv[0] = Ra1, pv[1] = Qg8

Instead reordering as the current patch we end up in:

pv[0] = Ra1, pv[1] = Nxf4

preserving the first searched move.

No functional change in single PV.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 18:00:47 +01:00
Marco Costalba
409930e98c Small cleanup of previous patches
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 18:00:22 +01:00
Joona Kiiski
b88f7df387 Reimplement MultiPV mode
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 06:47:33 +01:00
Joona Kiiski
adcfffceeb Reimplement support for "searchmoves" option
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 06:47:22 +01:00
Joona Kiiski
a3c1d64a5f Remove now unused RootMove.non_pv_score
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 06:47:06 +01:00
Joona Kiiski
ce24a229df Make root search to use standard MovePicker.
This patch temporarily breaks MultiPV and searchmove
features, but they will be re-implemented in future
patches.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-08-02 06:46:50 +01:00
Joona Kiiski
ce619b3b6c Don't probe TT at RootNode
In that case we should also update RootMoveList to avoid
bogus output

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-29 12:42:22 +01:00
Marco Costalba
2e2a4b4ea3 Fix pretty_pv() output in Chess960
And move it to search.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-26 12:39:15 +01:00
Joona Kiiski
7f519a2463 Fix PV output in Chess960
We missed to set chess960 flag into the std::stringstream used to
setup the PV line.

Bug introduced with commit f803f33e63
of 30/12/2010 when we started to print PV line into a std::stringstream
instead of directly into cout, where the chess960 flag is correctly set.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 18:27:29 +01:00
Marco Costalba
ff1ecb5d6c Tidy up benchmark.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 08:53:36 +01:00
Marco Costalba
dab1cd8af9 Rename execute_uci_command() to uci_loop()
As a side effect now root position can be directly
allocated on the stack and doesn't need to be defined
static anymore.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 08:12:07 +01:00
Marco Costalba
5b0c6b9bc0 Unhide the istringstream behind UCIParser
It is misnamed because it is not a parser, perhaps a
tokenizer, anyhow better call it for what it is, an
input string stream.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 07:39:07 +01:00
Marco Costalba
fc290dc30b Fix startpos_ply_counter() regression
Return the correct number of played plies at the end
of the setup moves. Currently it always returns 0 when
starting from start position, like in real games.

We fix this adding st->pliesFromNull that starts from 0
and is incremented after each setup move and is never
reset in the setup phase but only once search is started.

It is an hack because startpos_ply_counter() will return
different values during the search and is correct only
at the beginning of the search.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:59:14 +01:00
Marco Costalba
5f7eb20090 Use a circular buffer to keep track of setup states
This fixes a regression on real games due to the fact that
we have some mismatches:

    history[st->gamePly - i] != stp->key

when st->gamePly - i == 0,this is due to a nasty bug I have
introduced when using std::vector<> as StateInfo backup. The
point is that StateInfo keeps inside a pointer to the previous
StateInfo in a kind of linked list. But when std::vector<> is
resized reallocates a larger chunk of memory and moves the
data there so these pointers became stale.

This patch fixes the issue.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:15:40 +01:00
Marco Costalba
03ad183384 Don't update gamePly after each move
We just need startup value to calculate available
thinking time. So remove from state.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:15:39 +01:00
Marco Costalba
527a2ec541 Use std::vector<Move> to store UCI search moves
Avoid the ugly and anyhow incorrect hard limit on the
maximum number of moves and allow to handle an arbitrary
number of moves to search.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:15:38 +01:00
Marco Costalba
3185c36a65 Use st->gamePly to store fullMoves
This allow to retire do_setup_move() and also to simplify
draw detection logic becuase now we always have:

Min(st->rule50, st->gamePly) = st->rule50

This was already true when starting from starting position,
but now is true even when starting from a FEN string because
now we take in account fullmove number in counting gamePly so
that it is always.

st->rule50 <= st->gamePly

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:15:36 +01:00
Marco Costalba
3d8140a541 Retire history[]
Use key saved in state instead.

No functional change (in real games) and no speed regression.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:15:35 +01:00
Joona Kiiski
a6fc3d6ee5 Do not exit early even when seeing mate
Fixes the reported KNNK ending problem:

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

Joona says:

Now I finally had a time to take a look at on this issue.

I've reproduced the problem starting from this position:
1B6/1B2k3/P7/1P3p2/1K6/8/4b3/4b3 w - - 6 85

I made Stockfish play as white and Fruit as black.
I repeated test ten times and once SF was not able to deliver mate.

But I observed several times that SF had reported on last something like mate in 10.
However next time it played move with score mate in 15.

Easiest way to solve the problem is attached as a patch. I tested it several times and SF always
ended up playing the optimal move. Of course the downside is that now delivering mate
takes a bit longer, but IMO it's better to lose once in a while by time in sudden death
game than not being able to deliver simple mate with long time controls.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-24 06:13:29 +01:00
Marco Costalba
a1f9bf19d9 Revert previous patches due to bug
We have a bug (possibly because of returning draw from
root move list), it is possible to see when looking at
games with a GUI, we can see rarely but consistently the
score return as #0 for many depths until it comes back to
normal values.

Revert patches until it is not fixed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-20 03:34:39 +01:00
Joona Kiiski
4ad6a3496b Move the draw check also for qsearch
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-19 17:36:22 +01:00
Joona Kiiski
969ad8001c Move draw checks right after doing the move
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-19 17:36:14 +01:00
Marco Costalba
07e0dd27fb Small touches in set_option()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-17 12:55:26 +01:00
Marco Costalba
b8eb699db7 Validate input UCI moves
Running following command:

  position startpos moves e1e8

Makes SF to assert in debug mode in do_move() but to accept
bad input and continue in release mode where probably it is
going to crash little later.

So validate input before to feed do_move().

Suggestion by Yakovlev Vadim.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-17 10:22:55 +01:00
Marco Costalba
ad1f28bc1c Remove some useless include
Spotted by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-16 22:59:14 +01:00
Marco Costalba
67686b7684 Don't need to assert for pos.is_ok() when position is constant
It's only necessary to do the checking at the end of every non-const
member (including the constructors and from_fen()) of class Position.
Once the post-condition of every modifier guarantees the class invariant,
we don't need to verify sanity of the position as preconditions for outside
callers such as movegen, search etc. For non-class types such as Move and
Square we still need to assert of course.

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-16 10:53:34 +01:00
Marco Costalba
69f4954df1 Change hidden checkers API
After previous patch is no more needed to pass
the color, becuase it is always the side to move.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-16 10:12:46 +01:00
Marco Costalba
4894231ff7 Simplified discovered check connected_moves()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-16 09:50:35 +01:00
Marco Costalba
36bb57a47e Simplify and micro-optimize hidden_checkers()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-16 08:53:21 +01:00
Marco Costalba
a042449f5d No need to declare default Position c'tor
Pointed out by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-15 21:53:47 +01:00
Marco Costalba
f5e434c2d9 Fix a warning under MSVC
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-15 12:20:31 +01:00
Marco Costalba
37d551fb39 Fix parametrized direction in pawns generation
It worked by accident because we always called both directions,
but definition was wrong.

Functional change due to different generation order, but
perft numbers are the correct ones.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-15 12:13:55 +01:00
Marco Costalba
9c8c0de538 Cleanup handling of Delta enums
Ispired by Rein's code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-15 12:13:48 +01:00
Marco Costalba
fbdabe2975 Use std library to sort moves
Functional change due to the fact that now pick_best() is
stable, but should be no change in strenght.

Example code and ideas by Rein Halbersma.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-14 12:43:13 +01:00
Marco Costalba
0cfb30e5be Fix icc's "unreachable code" warning
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-11 11:17:50 +01:00
Marco Costalba
c081a81daf Teach to_fen() about Halfmove and Fullmove number
And also fix the last '/' at the end of the piece placement field.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-03 12:08:02 +01:00
Marco Costalba
155bed18f5 Use MoveList also in Position::move_is_pl_slow()
And rename it in Position::move_is_legal()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-03 11:00:28 +01:00
Marco Costalba
95d9687d95 Retire move_is_short_castle() and move_is_long_castle()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-03 10:27:15 +01:00
Marco Costalba
ff41b8df76 Restore startpos_ply_counter() instead of full_moves()
And pass correct currentPly to TimeManager::init().

This restores old behaviour, in particular now black has
a different timing than white becuase is no more:

currentPly = 2 * fullMoveNumber;

but becomes

2 * (fullMoves - 1) + int(sideToMove == BLACK)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-03 06:54:46 +01:00
Marco Costalba
53ccba8457 Introduce and use struct MoveList
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-02 14:04:33 +01:00
Marco Costalba
7ac6e3b850 Remove MSVC debug window hack in bench
Interference with profile-build under mingw

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-02 11:52:22 +01:00
Marco Costalba
d15217b953 Rearrange structs to avoid internal padding
Found with gcc -Wpadded gcc option.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-02 11:01:12 +01:00
Marco Costalba
bc54a44010 Revert PHQ-2
No clear advantage again standard, possibly we will
retest before to release.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-07-02 09:47:06 +01:00
Marco Costalba
3f806df0ca Small touches to do/undo_castle_move()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-30 20:04:46 +01:00
Marco Costalba
f3799aa750 Better document generate_castle_moves()
No functional change also in Chess960

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-30 19:58:44 +01:00
Marco Costalba
30ca6935a5 Small tweaks to search()
No functional change also in faked split mode

To be sure verified in real games with 4 threads TC 2"+0.1
After 11125 games 2497 - 2469 - 6159 ELO +0 (+- 4.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-30 06:54:09 +01:00
Marco Costalba
f25582d4b8 Remove duplicated enum Phase definition
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-29 17:54:12 +01:00
Marco Costalba
fb2fdb21d3 Retire find_checkers()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 18:01:51 +01:00
Marco Costalba
31a0d2200c Retire square_is_weak()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:22:33 +01:00
Marco Costalba
e0a00c4996 Retire one piece_list() overload
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:11:03 +01:00
Marco Costalba
e5077dc11e Rename pieces_of_color() in pieces()
To be uniform with other overloads.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:10:44 +01:00
Marco Costalba
01a191936e Retire redundant square_is_occupied()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:10:27 +01:00
Marco Costalba
8094b2add8 Change Position::pst() signature
To be more clear what is the underlying table.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:10:18 +01:00
Marco Costalba
ffb638995d Fix Shredder-FEN regression in from_fen()
Fix also an incredible 3% speed regression by an almost
never called function. I guess this is due to mingw very low
quality standard libraries implementation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-28 17:10:05 +01:00
Marco Costalba
c2a4856f9e Greatly simplify castling rights handling
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-27 19:26:30 +01:00
Marco Costalba
c9b24c3358 Assume input FEN string is correct in from_fen()
And also tolerate a 0 value for full move number.

Revert BUG_41 patch, now we set initial King file only
if a castling is possible, so we don't need the fix
anymore in case of correct FEN.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-27 12:08:12 +01:00
Marco Costalba
9305983018 Fix a bug in Position::is_ok()
If we cannot castle castleRightsMask[] could be not valid,
for instance when king initial file is FILE_A as queen rook.

In this case castleRightsMask[] at initialQRFile is different
from the expected (ALL_CASTLES ^ WHITE_OOO).

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-26 11:44:12 +01:00
Marco Costalba
ae2f5f25cd Rename type_of_piece() and color_of_piece()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-26 10:52:42 +01:00
Marco Costalba
923b14afaa Retire Position::color_of_piece_on()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-26 10:37:13 +01:00
Marco Costalba
a9782b94e6 Retire Position::type_of_piece_on()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-26 10:28:54 +01:00
Marco Costalba
351ef5c85b Retire seeValues[] and move PieceValue[] out of Position
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-26 10:19:37 +01:00
Marco Costalba
be2925b3c5 Restore user weights to 100
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-24 10:31:03 +01:00
Marco Costalba
ace27831ee PHQ settings for King and Mobility
See:
http://talkchess.com/forum/viewtopic.php?t=39327

After 8130 games on QUAD at 20"+0.1
1342 - 1359 - 5429 ELO +0 (+- 4.4)

Tried also version with just king settings changed:
After 5932 games 962 - 1052 - 3918 ELO -5 (+- 5.2)

And with just mobility settings changed:
After 4114 games 618 - 619  - 2877 ELO +0 (+- 5.9)

Frank has tested only 1200 games, but at longer TC and
against many engines, so because PHQ results are not worst
than other combination and not worst than original let's
commit his version.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-24 09:47:30 +01:00
Marco Costalba
6a1707889c Fix move_is_capture() to detect capture promotions
We miss to account as a capture a promotion capture !

Incredible bug in this critical function that is here
since a long time (b50921fd5c of 21/10/2009 !!)

This patch fixes the bug and readds the faster
move_is_capture_or_promotion() that slightly increases
overall speed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-23 12:09:31 +01:00
Marco Costalba
1c42d15340 Rewrite how uci info is sent to GUI
It is now much more modular than before and also we
always send the seldepth when we send the depth, this
avoids to make seldepth disappearing from GUI at the
start of a new iteration.

Print also fails high/low pv lines at high enough
search depths.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-22 17:32:22 +01:00
Marco Costalba
fe26967ea0 Simplify sliding_attacks()
Easy, almost trivial simplification, I don't understand
how I missed this before !!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-21 19:01:00 +01:00
Marco Costalba
e7413417ce Retire ksq from CheckInfo
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-21 12:13:26 +01:00
Marco Costalba
018022b866 Use CheckInfo to store pinned bitboard
This trivial change gives an impressive 2,5% speedup !!!!

Also retire one unused move_gives_check() overload.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-20 12:09:11 +01:00
Marco Costalba
7ea38e980b Omit mate distance pruning at root
Restore original behaviour, before root unification and
remove a now useless ugly hack for alpha in multi-pv case.

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-18 17:11:09 +01:00
Marco Costalba
8173d92dd2 Use an array index instead of an iterator in root list
It is not correct to use an iterator stick on a vector that
is sorted becuase iterator is invalidated in general case.

It happens to work by accident because iterators are implemented
as pointers and so they behave in the same (correct) way then
using array indices, but the latters are the correct thing to use.

Also better document the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-18 08:03:59 +01:00
Marco Costalba
808a4fe817 Remove useless bestValue = alpha assignement
It is a fossil from the root_search() era, no more
needed today.

Spotted by Onno

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-15 12:49:59 +01:00
Marco Costalba
72760c05c6 Try only recaptures in qsearch if depth is very low
This avoids search explosion in qsearch for some
patological cases like:

r1n1n1b1/1P1P1P1P/1N1N1N2/2RnQrRq/2pKp3/3BNQbQ/k7/4Bq2 w - - 0 1

After 9078 games 20"+0.1 QUAD:
Mod vs Orig 1413 - 1319 - 6346 ELO +3 (+- 4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-15 12:17:49 +01:00
Marco Costalba
15683034a7 Speed up kpk initialization
The trick is to classify more position at first cycle,
so to reduce following work. Speed up is of about 50% !

Also some cleanup while there.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-15 12:11:35 +01:00
Marco Costalba
4c9d570e43 Use Carry-Rippler trick to speed up magics
Nice trick discovered on:

http://chessprogramming.wikispaces.com/Traversing+Subsets+of+a+Set

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-15 02:15:41 +01:00
Marco Costalba
fc519ca74a Retire init_piece_square_tables()
Merge in init_zobrist() and rename the latter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-13 17:28:09 +01:00
Marco Costalba
a24c0a736c Increase LMR limit by one ply
Seems there is no regression so prefer to prune less.

After 8278 games
Mod vs Orig 1246 - 1265 - 5767 +0 ELO (+- 4.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-12 21:14:07 +01:00
Marco Costalba
b3a0b389d2 Better self-document init_zobrist()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-12 11:50:19 +01:00
Marco Costalba
47959c56fd Fix initialization of BSFTable[]
We should start from i = 0, it works by accident because
static storage BSFTable[] is init to zero by default.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-12 11:37:02 +01:00
Marco Costalba
aee75ae105 Don't update_gains() in qsearch
Is almost unuseful becuase captures are skipped.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-12 07:09:30 +01:00
Marco Costalba
4b8a7f2793 Fix score_captures() for the case of capture promotions
In case we have more than one promotion move, prefer
the one that captures the biggest piece.

Almost no functional change, anyhow I don't expect any
ELO change, it is just the correct thing to do.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-11 22:12:13 +01:00
Marco Costalba
89ec224cb9 Retire some unused functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-11 15:56:12 +01:00
Marco Costalba
b21a5e2f06 Micro-optimize castling handling in do_move()
And better self-document the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-11 15:31:39 +01:00
Marco Costalba
735cac5d53 Retire PieceLetters struct
Use a much simpler std::string instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-10 17:12:10 +01:00
Marco Costalba
bc4f3155ae Better document move_to_san()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-10 11:59:53 +01:00
Marco Costalba
a21a110188 Revert refinedValue in ProbCut
It seems much worst in number of nodes seacrhed to reach
the depth and anyhow does not give any advantage to the
Onno's oroginal one.

So revert by now and perhaps readd when we find something
clearly better.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-09 21:59:24 +01:00
Marco Costalba
b414fc0dfd Use double rotate for magic generation
Allow to choose among 4096 instances of pseudo-random
sequences instead of the previous 64 so the probability
to find a better sequence increases and actually we have
a much better 64 bit case and we can also use the 64 bit
version of pick_magic() also for 32 bits and althoug sub
optimal, because now we can have more choices results are
even slightly better also for 32 bit.

Use also a faster submask().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-09 18:47:58 +01:00
Marco Costalba
e0215f3222 Use refinedValue in ProbCut condition
After 12613 games at 20"+0.1 on QUAD
Mod vs Orig 1870 - 1863 - 8880 ELO +0 (+- 3.3)

So no performance change but it is a code semplification
and also is more easy to understand.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-08 18:03:26 +01:00
Marco Costalba
3dfff5bdae Small pick_magic() touches
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-08 17:59:47 +01:00
Marco Costalba
d632e77058 Find magics on the fly
Good result for 32 bit case where computation is very fast,
still not satisfying on 64 bit case where the magics seem
a bit harder to get.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-07 20:59:12 +01:00
Marco Costalba
6d665b7f78 Partially revert previous patches
Due to a -2% speed penalty. This patch takes the best
of the previous series without the regression due to
introduction of Magic struct.

Speedup against previous revision is of almost 3% !!!!

No functional change both in 32 and 64 bits.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-06 11:56:17 +01:00
Marco Costalba
b1b0c64046 Skip offset calculation in slider attacks
Another small simplification and micro optimization.

No functional change in both 32 and 64 bits.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-05 15:01:22 +01:00
Marco Costalba
670cee44f0 Get rid of Shift[] tables
We can calculate them counting the masks bits.

Also small tweak to sliding_attacks()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-05 14:13:41 +01:00
Marco Costalba
3092f0c646 Better name and document magic botboard stuff
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-05 12:28:36 +01:00
Marco Costalba
2f6142cb9b Try to keep memory access in the same cache line
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-05 12:27:53 +01:00
Marco Costalba
3b2bcee0a8 Skip draw by repetition check in qsearch
Cut in half the time spent in pos.draw() that accounts
for a whopping 1% of total time !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-04 11:29:54 +01:00
Marco Costalba
91407f4f74 Move bitboards initializations under one function
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-04 11:05:12 +01:00
Marco Costalba
025d57855a Calculate Bit Scan tables at initialization
Instead of hard-coding them.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-04 09:59:18 +01:00
Marco Costalba
8647fbd6ed Do not sort negative non captures at low depth
Speedup of the whole 3 patch series is of 2,5% !!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-04 06:35:24 +01:00
Marco Costalba
811037c845 Split non capture in two sets when ordering
But keep same ordering. This patch is prerequisite
for future work.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-03 22:10:23 +01:00
Marco Costalba
181cc3f93f Inline extension()
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-03 13:14:03 +01:00
Marco Costalba
91b919fd1d Use TT also in Root nodes
And other small stuff ti be tested in SMP

No functional change in single thread.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-03 12:59:50 +01:00
Marco Costalba
5ff6289afd Microoptimize generate<MV_EVASION>
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-06-02 12:49:40 +01:00
Marco Costalba
fca0a2dd88 New extended probcut implementation
Here the idea is to test probcut not only after bad
captures, but after any bad move, i.e. any move that
leaves the opponent with a good capture.

Ported by a patch from Onno, the difference from
original version is that we have moved probcut after
null search.

After 7917 games 4 threads 20"+0.1
Mod vs Orig: 1261 - 1095 - 5561 ELO +7 (+- 4.2) LOS 96%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-30 20:49:04 +01:00
Marco Costalba
462a39ec49 Fix SAN disambiguation bug
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-29 10:52:03 +01:00
Marco Costalba
70125a3be0 Rename PH_TT_MOVES in PH_TT_MOVE
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-29 09:17:03 +01:00
Marco Costalba
cff8877a1a Retire mateKiller
Practically useless:
After 6456 games 1281 - 1293 - 3882 ELO +0 (+- 5.5)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-28 22:50:22 +01:00
Marco Costalba
8f51f09de7 Unify MovePickerExt template parameters
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-28 13:23:32 +01:00
Marco Costalba
013dc43d5d Unify search() template parameters
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-28 12:44:39 +01:00
Marco Costalba
853e2a9495 Fix moveCount after legality check delay
We really want PV moves and also Split Point moves to be
legal to avoid messing the move counter and corresonding
PV move detection or shared Split Point's counter variable.

This fixes a real bug where a position with only one move
allowed returns bestValue == -VALUE_INFINITE if the move
turns out to be illegal.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-28 11:16:00 +01:00
Marco Costalba
4c3a000211 A bit of reformatting after previous series
And some documentation update.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-24 21:24:40 +01:00
Marco Costalba
9884573561 Test for legality only after futility pruning
Although there is a small functional change it seems
an improvment of about 2% in speed !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-23 20:21:02 +01:00
Marco Costalba
4b232f5ddc Move legal check out of MovePicker
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-23 20:20:52 +01:00
Marco Costalba
45ce92b89c Rename move_is_legal() in move_is_pl()
We disjoint pseudo legal detection from full legal detection.

It will be used by future patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-23 20:20:42 +01:00
Marco Costalba
bc86668ba4 Output debug info to cerr
So to be clearly visible when redirecting stdout to /dev/null

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-23 20:20:31 +01:00
Marco Costalba
0783211950 Fix a shadowed variable warning under gcc
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-23 20:20:19 +01:00
Marco Costalba
13fe7ee4df Bug wrong evasion detection for king moves
When we are in check and we move the king then testing with
pl_move_is_legal(m, pinned) is not enough becuase we cannot
rely on attackers_to() but we have to explicitly remove the
king form the occupied bitboard to catch as invalid moves like
b1a1 when opposite queen is on c1.

Our move generator already produces correct evasions so we
just need to add the extra verification to move_is_legal().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-22 11:51:28 +01:00
Marco Costalba
21fc66c246 Add file distance condition in move_is_legal()
Found another missed control in move_is_legal() thanks to
brute force testing.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-22 09:49:44 +01:00
Marco Costalba
ff9e49bac9 Remove useless casts in types.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-22 08:52:39 +01:00
Marco Costalba
90e83fa879 Promotion piece must be empty if is not a promotion
Add a new check in  move_is_legal()

Avoid useless casting in move.h while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-22 08:48:19 +01:00
Marco Costalba
3ef4fdeaa0 Introduce MovePicker::isBadCapture() and use in probcut
Small functional change due to the fact that now we skip
probcut on evasions.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-21 10:40:36 +01:00
Marco Costalba
13d8af1852 Correctly handle castle in see()
Suggested by Onno.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-20 06:58:53 +01:00
Marco Costalba
5b7a141065 Fix brekage from previous patches
It is interesting the fact that we need to test for
move_is_castle(m) anyway and not relying on testing
if destination square is attacked. Indeed the latter
condition fails if the castling rook is attacked,
castling is coded as "king captures the rook" but it
is legal in that case.

Verified no functional change with beginning of the series.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-18 07:05:36 +01:00
Marco Costalba
89a06f6651 Micro-optimize pl_move_is_legal()
Remove the check for castling moves because it is
already implicit in the check for king moves and castling
is so rare that doing the check is just a slow down.

Thanks to Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 23:47:26 +01:00
Marco Costalba
a2e924039b Retire move_is_capture_or_promotion()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 23:39:14 +01:00
Marco Costalba
85d1f9c5ec Fix move_is_capture() definition
The structure of move is changed so should also the two
functions. It happens that it works by accident !

Bug spotted by Marek Kwiatkowski

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 23:31:40 +01:00
Marco Costalba
e444e18d2b Retire test for king moves in see()
We already test this condition in see_sign() and
so it is almost always a redundant verification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 12:25:49 +01:00
Marco Costalba
ad0fdf0da6 Retire Position::see(Square from, Square to) overload
Alomst unuseful.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 12:17:57 +01:00
Marco Costalba
fbbc7e421c Prefer an assert to a comment in position.h
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 12:17:48 +01:00
Marco Costalba
1cc42ac9e4 Let 'make' with no arguments to show compilation options
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 12:17:38 +01:00
Marco Costalba
6624105b5b Prefer ttMove to tte->move() in search()
Avoids aliasing problems due to TT overwrites.

Node changes becuase of IID.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-17 12:17:29 +01:00
Marco Costalba
de58594b0f Use standard naming conventions in book.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-08 11:50:38 +01:00
Marco Costalba
d5f0b91c06 Reintroduce operator>>() in Book class
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-08 09:15:18 +01:00
Marco Costalba
f78488bbdb Restore development version
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-08 09:11:36 +01:00
Marco Costalba
e3b23eb818 Stockfish 2.1.1
stockfish bench signature is: 6487630

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-08 08:46:33 +01:00
Marco Costalba
d494725400 Spell fix in evaluate.cpp
Spotted by Eelco.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-07 10:08:53 +01:00
Marco Costalba
2535dc1340 Fix reading of book file
Bug is subtle because appears only under MSVC 32 bits in
optimized version, hence was missed before.

Bug is due to the fact that evaluation order of terms of a
sum is undefined by the standard, so in get_int() we have:

return 256 * get_int<n-1>() + bookFile.get();

And if get() is evaluated before get_int() we have a corrupted
key.

The patch rewrites the code in a more natural and predictable way.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-07 09:51:33 +01:00
Marco Costalba
339bf9b7e1 Remove redundant assignment in search()
It is already assigned few lines before.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-05 12:16:26 +01:00
Marco Costalba
bb86691a0d Restore development version
No functional change.
2011-05-05 06:35:42 +01:00
Marco Costalba
f7fee4c616 Fix a warning in debug mode
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-05-03 19:29:21 +01:00
45 changed files with 4354 additions and 5397 deletions

View File

@@ -1,22 +1,20 @@
1. Introduction
---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI
(like XBoard with PolyGlot, eboard, Josè, Arena, Sigma Chess, Shredder,
Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
Stockfish with your GUI.
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is not a
complete chess program, but requires some UCI compatible GUI (like XBoard
with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz)
in order to be used comfortably. Read the documentation for your GUI of choice
for information about how to use Stockfish with your GUI.
This version of Stockfish supports up to 32 CPUs, but has not been
tested thoroughly with more than 4. The program tries to detect the
number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always
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 four threads, it
is recommended to raise the value of "Minimum Split Depth" UCI parameter
to 6.
This version of Stockfish supports up to 32 CPUs, but has not been tested
thoroughly with more than 4. The program tries to detect the number of
CPUs on your computer and set the number of search threads accordingly, but
please be aware that the detection is not always 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
"Min Split Depth" UCI parameter to 7.
2. Files
@@ -30,9 +28,9 @@ This distribution of Stockfish consists of the following files:
License.
* src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like
systems. For further information about how to compile Stockfish
yourself, read section 4 below.
Makefile that can be used to compile Stockfish on Unix-like systems.
For further information about how to compile Stockfish yourself
read section 4 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter.
@@ -50,29 +48,23 @@ parameter "Book File".
4. Compiling it yourself
------------------------
On Unix-like systems, it should usually be possible to compile
Stockfish directly from the source code with the included Makefile.
On Unix-like systems, it should usually be possible to compile Stockfish
directly from the source code with the included Makefile.
For big-endian machines like Power PC you need to enable the proper
flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
Stockfish has POPCNT instruction runtime detection and support. This can
give an extra speed on Core i7 or similar systems. To enable this feature
compile with 'make icc-profile-popcnt'
On 64 bit systems the 'bsfq' assembly instruction will be used for bit
counting. Detection is automatic at compile time, but in case you experience
compile problems you can comment out #define USE_BSFQ line in types.h
Stockfish has support for 32 or 64 bits CPUS, big-endian machines, like
Power PC, hardware POPCNT instruction and other platforms.
In general is recommended to run 'make help' to see a list of make targets
with corresponding descriptions.
with corresponding descriptions. When not using Makefile to compile, for
instance with Microsoft MSVC, you need to manually set/unset in the compiler
command line some swicthes, see file types.h for a quick reference.
5. Terms of use
---------------
Stockfish is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly
(GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or
@@ -80,9 +72,8 @@ using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Stockfish in
some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the
to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named
Copying.txt.

View File

@@ -15,13 +15,10 @@ ResignScore = 600
[Engine]
Hash = 128
Threads = 1
OwnBook = false
Book File = book.bin
Best Book Move = false
Use Search Log = false
Search Log Filename = SearchLog.txt
Book File = book.bin
Best Book Move = false
Mobility (Middle Game) = 100
Mobility (Endgame) = 100
Passed Pawns (Middle Game) = 100
@@ -29,11 +26,18 @@ Passed Pawns (Endgame) = 100
Space = 100
Aggressiveness = 100
Cowardice = 100
Minimum Split Depth = 4
Maximum Number of Threads per Split Point = 5
Min Split Depth = 4
Max Threads per Split Point = 5
Threads = 1
Use Sleeping Threads = false
Hash = 128
Ponder = true
OwnBook = false
MultiPV = 1
Skill Level = 20
Emergency Move Horizon = 40
Emergency Base Time = 200
Emergency Move Time = 70
Minimum Thinking Time = 20
UCI_Chess960 = false
UCI_AnalyseMode = false

View File

@@ -1,6 +1,6 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
# Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -42,16 +42,16 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
# flag --- Comp switch --- Description
# ----------------------------------------------------------------------------
#
# debug = no/yes --- -DNDEBUG --- Enable/Disable debug mode
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = no/yes --- -DBIGENDIAN --- big/little-endian byte order
# prefetch = no/yes --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = no/yes --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction
# --- (Works only with GCC and ICC 64-bit)
# popcnt = no/yes --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = yes/no --- -DBIGENDIAN --- big/little-endian byte order
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only
# with GCC and ICC 64-bit)
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
@@ -308,16 +308,25 @@ endif
### 3.10 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -DUSE_POPCNT
CXXFLAGS += -msse3 -DUSE_POPCNT
endif
### 3.11 Link Time Optimization, it works since gcc 4.5 but not on mingw.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
GCC_MAJOR := `gcc -dumpversion | cut -f1 -d.`
GCC_MINOR := `gcc -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
### ==========================================================================
### Section 4. Public targets
### ==========================================================================
default:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) build
help:
@echo ""
@echo "To compile stockfish, type: "
@@ -328,7 +337,7 @@ help:
@echo ""
@echo "build > Build unoptimized version"
@echo "profile-build > Build PGO-optimized version"
@echo "popcnt-profile-build > Build PGO-optimized version with optional popcnt-support"
@echo "double-profile-build > Build PGO-optimized version with and without popcnt support"
@echo "strip > Strip executable"
@echo "install > Install executable"
@echo "clean > Clean up"
@@ -337,7 +346,7 @@ help:
@echo "Supported archs:"
@echo ""
@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 runtime support for popcnt instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@@ -389,7 +398,7 @@ profile-build:
@echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
popcnt-profile-build:
double-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
@echo ""
@echo "Step 0/6. Preparing for profile build."
@@ -431,6 +440,9 @@ clean:
testrun:
@$(PGOBENCH)
default:
help
### ==========================================================================
### Section 5. Private targets
### ==========================================================================
@@ -484,6 +496,7 @@ gcc-profile-make:
gcc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-clean:

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,16 +21,19 @@
#include <iostream>
#include <vector>
#include "misc.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace std;
using namespace Search;
static const string Defaults[] = {
static const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -43,30 +46,24 @@ static const string Defaults[] = {
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
""
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
};
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the
/// of positions for a given limit each. There are five parameters; the
/// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default
/// is ply 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type
/// of the limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt.
/// be used, the limit value spent for each position (optional, default is
/// depth 12), an optional file name where to look for positions in fen
/// format (defaults are the positions defined above) and the type of the
/// limit value: depth (default), time in secs or number of nodes.
void benchmark(int argc, char* argv[]) {
vector<string> fenList;
SearchLimits limits;
int64_t totalNodes;
vector<string> fens;
LimitsType limits;
int time;
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
int64_t nodes = 0;
// Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128";
@@ -75,79 +72,64 @@ void benchmark(int argc, char* argv[]) {
string fenFile = argc > 5 ? argv[5] : "default";
string valType = argc > 6 ? argv[6] : "depth";
Options["Hash"].set_value(ttSize);
Options["Threads"].set_value(threads);
Options["OwnBook"].set_value("false");
Options["Hash"] = ttSize;
Options["Threads"] = threads;
Options["OwnBook"] = false;
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else
limits.maxDepth = atoi(valStr.c_str());
// Do we need to load positions from a given FEN file ?
if (fenFile != "default")
{
string fen;
ifstream f(fenFile.c_str());
ifstream file(fenFile.c_str());
if (f.is_open())
if (!file.is_open())
{
fenList.clear();
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
cerr << "Unable to open file " << fenFile << endl;
exit(EXIT_FAILURE);
}
while (getline(file, fen))
if (!fen.empty())
fens.push_back(fen);
file.close();
}
else
fens.assign(Defaults, Defaults + 16);
// Ok, let's start the benchmark !
totalNodes = 0;
time = get_system_time();
time = system_time();
for (size_t i = 0; i < fenList.size(); i++)
for (size_t i = 0; i < fens.size(); i++)
{
Move moves[] = { MOVE_NONE };
Position pos(fenList[i], false, 0);
Position pos(fens[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (valType == "perft")
{
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
totalNodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
else
{
if (!think(pos, limits, moves))
break;
totalNodes += pos.nodes_searched();
Threads.start_thinking(pos, limits, vector<Move>(), false);
nodes += RootPosition.nodes_searched();
}
}
time = get_system_time() - time;
time = system_time() - time;
cerr << "\n==============================="
cerr << "\n==========================="
<< "\nTotal time (ms) : " << time
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << endl;
// MS Visual C++ debug window always unconditionally closes when program
// exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl;
cin >> time;
#endif
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << int(nodes / (time / 1000.0)) << endl;
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,31 +28,31 @@ namespace {
RESULT_UNKNOWN,
RESULT_INVALID,
RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW
};
struct KPKPosition {
Result classify_knowns(int index);
Result classify(int index, Result db[]);
private:
void from_index(int index);
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const { return StepAttacksBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[WP][pawnSquare]; }
Result classify_white(const Result db[]);
Result classify_black(const Result db[]);
Bitboard wk_attacks() const { return StepAttacksBB[W_KING][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[B_KING][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[W_PAWN][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
Result classify_wtm(const KPKPosition& pos, const Result bb[]);
Result classify_btm(const KPKPosition& pos, const Result bb[]);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
}
@@ -65,64 +65,49 @@ uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
}
void init_kpk_bitbase() {
void kpk_bitbase_init() {
Result bb[IndexMax];
Result db[IndexMax];
KPKPosition pos;
bool repeat;
int index, bit, repeat = 1;
// Initialize table
for (int i = 0; i < IndexMax; i++)
{
pos.from_index(i);
bb[i] = !pos.is_legal() ? RESULT_INVALID
: pos.is_immediate_draw() ? RESULT_DRAW
: pos.is_immediate_win() ? RESULT_WIN : RESULT_UNKNOWN;
}
for (index = 0; index < IndexMax; index++)
db[index] = pos.classify_knowns(index);
// Iterate until all positions are classified (30 cycles needed)
do {
repeat = false;
for (int i = 0; i < IndexMax; i++)
if (bb[i] == RESULT_UNKNOWN)
{
pos.from_index(i);
bb[i] = (pos.sideToMove == WHITE) ? classify_wtm(pos, bb)
: classify_btm(pos, bb);
if (bb[i] != RESULT_UNKNOWN)
repeat = true;
}
} while (repeat);
while (repeat)
for (repeat = index = 0; index < IndexMax; index++)
if ( db[index] == RESULT_UNKNOWN
&& pos.classify(index, db) != RESULT_UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++)
for (int j = 0; j < 32; j++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS)
KPKBitbase[i] |= (1 << j);
for (index = 0; index < IndexMax / 32; index++)
for (bit = 0; bit < 32; bit++)
if (db[32 * index + bit] == RESULT_WIN)
KPKBitbase[index] |= (1 << bit);
}
namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
assert(square_file(wpsq) <= FILE_D);
assert(file_of(wpsq) <= FILE_D);
int p = int(square_file(wpsq)) + 4 * int(square_rank(wpsq) - 1);
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
int p = file_of(wpsq) + 4 * (rank_of(wpsq) - 1);
int r = stm + 2 * bksq + 128 * wksq + 8192 * p;
assert(r >= 0 && r < IndexMax);
@@ -131,73 +116,79 @@ namespace {
void KPKPosition::from_index(int index) {
int s = (index / 8192) % 24;
sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64);
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
int s = index >> 13;
sideToMove = Color(index & 1);
blackKingSquare = Square((index >> 1) & 63);
whiteKingSquare = Square((index >> 7) & 63);
pawnSquare = make_square(File(s & 3), Rank((s >> 2) + 1));
}
bool KPKPosition::is_legal() const {
Result KPKPosition::classify_knowns(int index) {
from_index(index);
// Check if two pieces are on the same square
if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return false;
return RESULT_INVALID;
if (sideToMove == WHITE)
{
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| bit_is_set(pawn_attacks(), blackKingSquare))
return false;
}
else if (bit_is_set(bk_attacks(), whiteKingSquare))
return false;
return true;
}
bool KPKPosition::is_immediate_draw() const {
if (sideToMove == BLACK)
{
Bitboard wka = wk_attacks();
Bitboard bka = bk_attacks();
// Case 1: Stalemate
if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB)
return true;
// Case 2: King can capture pawn
if (bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true;
}
else
{
// Case 1: Stalemate (possible pawn files are only from A to D)
if ( whiteKingSquare == SQ_A8
&& pawnSquare == SQ_A7
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true;
}
return false;
}
bool KPKPosition::is_immediate_win() const {
// Check if a king can be captured
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| (bit_is_set(pawn_attacks(), blackKingSquare) && sideToMove == WHITE))
return RESULT_INVALID;
// The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured.
return sideToMove == WHITE
&& square_rank(pawnSquare) == RANK_7
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N));
if ( rank_of(pawnSquare) == RANK_7
&& sideToMove == WHITE
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N)))
return RESULT_WIN;
// Check for known draw positions
//
// Case 1: Stalemate
if ( sideToMove == BLACK
&& !(bk_attacks() & ~(wk_attacks() | pawn_attacks())))
return RESULT_DRAW;
// Case 2: King can capture pawn
if ( sideToMove == BLACK
&& bit_is_set(bk_attacks(), pawnSquare) && !bit_is_set(wk_attacks(), pawnSquare))
return RESULT_DRAW;
// Case 3: Black king in front of white pawn
if ( blackKingSquare == pawnSquare + DELTA_N
&& rank_of(pawnSquare) < RANK_7)
return RESULT_DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( whiteKingSquare == pawnSquare + DELTA_N
&& blackKingSquare == pawnSquare + DELTA_N + DELTA_N + DELTA_N
&& rank_of(pawnSquare) < RANK_5
&& sideToMove == WHITE)
return RESULT_DRAW;
// Case 5: Stalemate with rook pawn
if ( blackKingSquare == SQ_A8
&& file_of(pawnSquare) == FILE_A)
return RESULT_DRAW;
return RESULT_UNKNOWN;
}
Result classify_wtm(const KPKPosition& pos, const Result bb[]) {
Result KPKPosition::classify(int index, Result db[]) {
// If one move leads to a position classified as RESULT_LOSS, the result
from_index(index);
db[index] = (sideToMove == WHITE ? classify_white(db) : classify_black(db));
return db[index];
}
Result KPKPosition::classify_white(const Result db[]) {
// If one move leads to a position classified as RESULT_WIN, the result
// of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN.
@@ -208,13 +199,13 @@ namespace {
Result r;
// King moves
b = pos.wk_attacks();
b = wk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = bb[compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK)];
r = db[compute_index(s, blackKingSquare, pawnSquare, BLACK)];
if (r == RESULT_LOSS)
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
@@ -222,26 +213,24 @@ namespace {
}
// Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7)
if (rank_of(pawnSquare) < RANK_7)
{
s = pos.pawnSquare + DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
s = pawnSquare + DELTA_N;
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
// Double pawn push
if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
if (rank_of(s) == RANK_3 && r != RESULT_INVALID)
{
s += DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
@@ -251,14 +240,12 @@ namespace {
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
}
Result classify_btm(const KPKPosition& pos, const Result bb[]) {
Result KPKPosition::classify_black(const Result db[]) {
// If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the current position is classified as
// RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
// classified as RESULT_WIN, the position is classified as RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
@@ -266,11 +253,11 @@ namespace {
Result r;
// King moves
b = pos.bk_attacks();
b = bk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = bb[compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE)];
r = db[compute_index(whiteKingSquare, s, pawnSquare, WHITE)];
if (r == RESULT_DRAW)
return RESULT_DRAW;
@@ -278,7 +265,7 @@ namespace {
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS;
return unknownFound ? RESULT_UNKNOWN : RESULT_WIN;
}
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,159 +17,27 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cstring>
#include <iostream>
#include "bitboard.h"
#include "bitcount.h"
#include "rkiss.h"
#if defined(IS_64BIT)
Bitboard RMasks[64];
Bitboard RMagics[64];
Bitboard* RAttacks[64];
int RShifts[64];
const uint64_t BMult[64] = {
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL,
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x00001004024D5000ULL, 0x0102044400800200ULL,
0x8108108820112000ULL, 0xA880818210C00046ULL, 0x4008008801082000ULL,
0x0060882404049400ULL, 0x0104402004240810ULL, 0x000A002084250200ULL,
0x00100B0880801100ULL, 0x0004080201220101ULL, 0x0044008080A00000ULL,
0x0000202200842000ULL, 0x5006004882D00808ULL, 0x0000200045080802ULL,
0x0086100020200601ULL, 0xA802080A20112C02ULL, 0x0080411218080900ULL,
0x000200A0880080A0ULL, 0x9A01010000104000ULL, 0x0028008003100080ULL,
0x0211021004480417ULL, 0x0401004188220806ULL, 0x00825051400C2006ULL,
0x00140C0210943000ULL, 0x0000242800300080ULL, 0x00C2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x0000822081014405ULL, 0x800C049E40400804ULL, 0x4A0404028A000820ULL,
0x0022060201041200ULL, 0x0360904200840801ULL, 0x0881A08208800400ULL,
0x0060202C00400420ULL, 0x1204440086061400ULL, 0x0008184042804040ULL,
0x0064040315300400ULL, 0x0C01008801090A00ULL, 0x0808010401140C00ULL,
0x04004830C2020040ULL, 0x0080005002020054ULL, 0x40000C14481A0490ULL,
0x0010500101042048ULL, 0x1010100200424000ULL, 0x0000640901901040ULL,
0x00000A0201014840ULL, 0x00840082AA011002ULL, 0x010010840084240AULL,
0x0420400810420608ULL, 0x8D40230408102100ULL, 0x4A00200612222409ULL,
0x0A08520292120600ULL
};
const uint64_t RMult[64] = {
0x0A8002C000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x0380180080141000ULL, 0x1A00060008211044ULL, 0x410001000A0C0008ULL,
0x9500060004008100ULL, 0x0100024284A20700ULL, 0x0000802140008000ULL,
0x0080C01002A00840ULL, 0x0402004282011020ULL, 0x9862000820420050ULL,
0x0001001448011100ULL, 0x6432800200800400ULL, 0x040100010002000CULL,
0x0002800D0010C080ULL, 0x90C0008000803042ULL, 0x4010004000200041ULL,
0x0003010010200040ULL, 0x0A40828028001000ULL, 0x0123010008000430ULL,
0x0024008004020080ULL, 0x0060040001104802ULL, 0x00582200028400D1ULL,
0x4000802080044000ULL, 0x0408208200420308ULL, 0x0610038080102000ULL,
0x3601000900100020ULL, 0x0000080080040180ULL, 0x00C2020080040080ULL,
0x0080084400100102ULL, 0x4022408200014401ULL, 0x0040052040800082ULL,
0x0B08200280804000ULL, 0x008A80A008801000ULL, 0x4000480080801000ULL,
0x0911808800801401ULL, 0x822A003002001894ULL, 0x401068091400108AULL,
0x000004A10A00004CULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x000100A000D50041ULL, 0x00810050020B0020ULL, 0x0204000800808004ULL,
0x00020048100A000CULL, 0x0112000831020004ULL, 0x0009000040810002ULL,
0x0440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4B824A2010010100ULL, 0x04080801810C0080ULL, 0x00000400802A0080ULL,
0x8224080110026400ULL, 0x40002C4104088200ULL, 0x01002100104A0282ULL,
0x1208400811048021ULL, 0x3201014A40D02001ULL, 0x0005100019200501ULL,
0x0101000208001005ULL, 0x0002008450080702ULL, 0x001002080301D00CULL,
0x410201CE5C030092ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844C6A22981ULL, 0x710358A6EA25C19EULL, 0x704F746D63A4A8DCULL,
0xBFED1A0B80F838C5ULL, 0x90561D5631E62110ULL, 0x2804260376E60944ULL,
0x84A656409AA76871ULL, 0xF0267F64C28B6197ULL, 0x70764EBB762F0585ULL,
0x92AA09E0CFE161DEULL, 0x41EE1F6BB266F60EULL, 0xDDCBF04F6039C444ULL,
0x5A3FAB7BAC0D988AULL, 0xD3727877FA4EAA03ULL, 0xD988402D868DDAAEULL,
0x812B291AFA075C7CULL, 0x94FAF987B685A932ULL, 0x3ED867D8470D08DBULL,
0x92517660B8901DE8ULL, 0x2D97E43E058814B4ULL, 0x880A10C220B25582ULL,
0xC7C6520D1F1A0477ULL, 0xDBFC7FBCD7656AA6ULL, 0x78B1B9BFB1A2B84FULL,
0x2F20037F112A0BC1ULL, 0x657171EA2269A916ULL, 0xC08302B07142210EULL,
0x0880A4403064080BULL, 0x3602420842208C00ULL, 0x852800DC7E0B6602ULL,
0x595A3FBBAA0F03B2ULL, 0x9F01411558159D5EULL, 0x2B4A4A5F88B394F2ULL,
0x4AFCBFFC292DD03AULL, 0x4A4094A3B3F10522ULL, 0xB06F00B491F30048ULL,
0xD5B3820280D77004ULL, 0x8B2E01E7C8E57A75ULL, 0x2D342794E886C2E6ULL,
0xC302C410CDE21461ULL, 0x111F426F1379C274ULL, 0xE0569220ABB31588ULL,
0x5026D3064D453324ULL, 0xE2076040C343CD8AULL, 0x93EFD1E1738021EEULL,
0xB680804BED143132ULL, 0x44E361B21986944CULL, 0x44C60170EF5C598CULL,
0xF4DA475C195C9C94ULL, 0xA3AFBB5F72060B1DULL, 0xBC75F410E41C4FFCULL,
0xB51C099390520922ULL, 0x902C011F8F8EC368ULL, 0x950B56B3D6F5490AULL,
0x3909E0635BF202D0ULL, 0x5744F90206EC10CCULL, 0xDC59FD76317ABBC1ULL,
0x881C7C67FCBFC4F6ULL, 0x47CA41E7E440D423ULL, 0xEB0C88112048D004ULL,
0x51C60E04359AEF1AULL, 0x1AA1FE0E957A5554ULL, 0xDD9448DB4F5E3104ULL,
0xDC01F6DCA4BEBBDCULL,
};
const uint64_t RMult[64] = {
0xD7445CDEC88002C0ULL, 0xD0A505C1F2001722ULL, 0xE065D1C896002182ULL,
0x9A8C41E75A000892ULL, 0x8900B10C89002AA8ULL, 0x9B28D1C1D60005A2ULL,
0x015D6C88DE002D9AULL, 0xB1DBFC802E8016A9ULL, 0x149A1042D9D60029ULL,
0xB9C08050599E002FULL, 0x132208C3AF300403ULL, 0xC1000CE2E9C50070ULL,
0x9D9AA13C99020012ULL, 0xB6B078DAF71E0046ULL, 0x9D880182FB6E002EULL,
0x52889F467E850037ULL, 0xDA6DC008D19A8480ULL, 0x468286034F902420ULL,
0x7140AC09DC54C020ULL, 0xD76FFFFA39548808ULL, 0xEA901C4141500808ULL,
0xC91004093F953A02ULL, 0x02882AFA8F6BB402ULL, 0xAEBE335692442C01ULL,
0x0E904A22079FB91EULL, 0x13A514851055F606ULL, 0x76C782018C8FE632ULL,
0x1DC012A9D116DA06ULL, 0x3C9E0037264FFFA6ULL, 0x2036002853C6E4A2ULL,
0xE3FE08500AFB47D4ULL, 0xF38AF25C86B025C2ULL, 0xC0800E2182CF9A40ULL,
0x72002480D1F60673ULL, 0x2500200BAE6E9B53ULL, 0xC60018C1EEFCA252ULL,
0x0600590473E3608AULL, 0x46002C4AB3FE51B2ULL, 0xA200011486BCC8D2ULL,
0xB680078095784C63ULL, 0x2742002639BF11AEULL, 0xC7D60021A5BDB142ULL,
0xC8C04016BB83D820ULL, 0xBD520028123B4842ULL, 0x9D1600344AC2A832ULL,
0x6A808005631C8A05ULL, 0x604600A148D5389AULL, 0xE2E40103D40DEA65ULL,
0x945B5A0087C62A81ULL, 0x012DC200CD82D28EULL, 0x2431C600B5F9EF76ULL,
0xFB142A006A9B314AULL, 0x06870E00A1C97D62ULL, 0x2A9DB2004A2689A2ULL,
0xD3594600CAF5D1A2ULL, 0xEE0E4900439344A7ULL, 0x89C4D266CA25007AULL,
0x3E0013A2743F97E3ULL, 0x0180E31A0431378AULL, 0x3A9E465A4D42A512ULL,
0x98D0A11A0C0D9CC2ULL, 0x8E711C1ABA19B01EULL, 0x8DCDC836DD201142ULL,
0x5AC08A4735370479ULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#endif // defined(IS_64BIT)
// Global bitboards definitions with static storage duration are
// automatically set to zero before enter main().
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard BMasks[64];
Bitboard BMagics[64];
Bitboard* BAttacks[64];
int BShifts[64];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8];
@@ -186,19 +54,18 @@ Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
int SquareDistance[64][64];
namespace {
void init_masks();
void init_step_attacks();
void init_pseudo_attacks();
void init_between_bitboards();
Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]);
CACHE_LINE_ALIGNMENT
int BSFTable[64];
Bitboard RookTable[0x19000]; // Storage space for rook attacks
Bitboard BishopTable[0x1480]; // Storage space for bishop attacks
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]);
}
@@ -211,7 +78,7 @@ void print_bitboard(Bitboard b) {
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? 'X' : ' ') << ' ';
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? "X " : " ");
std::cout << "|\n";
}
@@ -225,39 +92,22 @@ void print_bitboard(Bitboard b) {
#if defined(IS_64BIT) && !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26,
38, 15, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27,
33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61,
22, 43, 51, 60, 42, 59, 58
};
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
}
#elif !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5,
52, 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38,
28, 58, 20, 37, 17, 36, 8
};
Square first_1(Bitboard b) {
b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
}
// Use type-punning
@@ -284,12 +134,12 @@ Square pop_1st_bit(Bitboard* bb) {
if (u.dw.l)
{
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
ret = Square(BSFTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783A9B23) >> 26]);
u.dw.l &= (u.dw.l - 1);
*bb = u.b;
return ret;
}
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
ret = Square(BSFTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783A9B23) >> 26]);
u.dw.h &= (u.dw.h - 1);
*bb = u.b;
return ret;
@@ -298,189 +148,219 @@ Square pop_1st_bit(Bitboard* bb) {
#endif // !defined(USE_BSFQ)
/// init_bitboards() initializes various bitboard arrays. It is called during
/// bitboards_init() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
void bitboards_init() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
init_masks();
init_step_attacks();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
init_between_bitboards();
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = 1ULL << s;
ClearMaskBB[s] = ~SetMaskBB[s];
}
ClearMaskBB[SQ_NONE] = ~0ULL;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(file_of(s));
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(file_of(s));
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (int i = 0; i < 64; i++)
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
{
Bitboard b = 1ULL << i;
b ^= b - 1;
b ^= b >> 32;
BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i;
}
else
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (int k = 0; steps[pt][k]; k++)
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[make_piece(c, pt)][s], to);
}
init_magic_bitboards(ROOK, RAttacks, RMagics, RMasks, RShifts);
init_magic_bitboards(BISHOP, BAttacks, BMagics, BMasks, BShifts);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0);
RookPseudoAttacks[s] = rook_attacks_bb(s, 0);
QueenPseudoAttacks[s] = queen_attacks_bb(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
set_bit(&BetweenBB[s1][s2], s);
}
}
namespace {
// All functions below are used to precompute various bitboards during
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
Bitboard sliding_attacks(PieceType pt, Square sq, Bitboard occupied) {
void init_masks() {
SquaresByColorBB[DARK] = 0xAA55AA55AA55AA55ULL;
SquaresByColorBB[LIGHT] = ~SquaresByColorBB[DARK];
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
SetMaskBB[SQ_NONE] = EmptyBoardBB;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];
}
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s);
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
}
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
}
void init_step_attacks() {
const int step[][9] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}
};
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (Piece pc = WP; pc <= BK; pc++)
for (int k = 0; step[pc][k] != 0; k++)
{
Square to = s + Square(step[pc][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[pc][s], to);
}
}
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax) {
int dx, dy, f, r;
int rk = sq / 8;
int fl = sq % 8;
Bitboard attacks = EmptyBoardBB;
Square deltas[][4] = { { DELTA_N, DELTA_E, DELTA_S, DELTA_W },
{ DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW } };
Bitboard attacks = 0;
Square* delta = (pt == ROOK ? deltas[0] : deltas[1]);
for (int i = 0; i < 4; i++)
{
dx = deltas[i][0];
dy = deltas[i][1];
f = fl + dx;
r = rk + dy;
Square s = sq + delta[i];
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
while (square_is_ok(s) && square_distance(s, s - delta[i]) == 1)
{
attacks |= (1ULL << (f + r * 8));
set_bit(&attacks, s);
if (occupied & (1ULL << (f + r * 8)))
if (bit_is_set(occupied, s))
break;
f += dx;
r += dy;
s += delta[i];
}
}
return attacks;
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = EmptyBoardBB;
int sq, cnt = 0;
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
while (mask)
Bitboard magic;
// Values s1 and s2 are used to rotate the candidate magic of a
// quantity known to be the optimal to quickly find the magics.
int s1 = booster & 63, s2 = (booster >> 6) & 63;
while (true)
{
sq = pop_1st_bit(&mask);
magic = rk.rand<Bitboard>();
magic = (magic >> s1) | (magic << (64 - s1));
magic &= rk.rand<Bitboard>();
magic = (magic >> s2) | (magic << (64 - s2));
magic &= rk.rand<Bitboard>();
if (index & (1 << cnt++))
result |= (1ULL << sq);
}
return result;
}
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
Bitboard b, v;
int i, j, index;
for (i = index = 0; i < 64; i++)
{
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0, deltas, 1, 6, 1, 6);
j = 1 << ((CpuIs64Bit ? 64 : 32) - shift[i]);
for (int k = 0; k < j; k++)
{
b = index_to_bitboard(k, mask[i]);
v = CpuIs64Bit ? b * mult[i] : unsigned(b * mult[i] ^ (b >> 32) * (mult[i] >> 32));
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, deltas, 0, 7, 0, 7);
}
index += j;
if (BitCount8Bit[(mask * magic) >> 56] >= 6)
return magic;
}
}
void init_pseudo_attacks() {
// init_magic_bitboards() computes all rook and bishop magics at startup.
// Magic bitboards are used to look up attacks of sliding pieces. As reference
// see chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]) {
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, index, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = (pt == ROOK ? RookTable : BishopTable);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
// Board edges are not considered in the relevant occupancies
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
// Given a square 's', the mask is the bitboard of sliding attacks from
// 's' computed on an empty board. The index must be big enough to contain
// all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attacks(pt, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attacks bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attacks(pt, s, b);
b = (b - masks[s]) & masks[s];
} while (b);
// Set the offset for the table of the next square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size;
booster = MagicBoosters[Is64Bit][rank_of(s)];
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
do {
magics[s] = pick_random(masks[s], rk, booster);
memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side
// effect of verifying the magic.
for (i = 0; i < size; i++)
{
index = (pt == ROOK ? rook_index(s, occupancy[i])
: bishop_index(s, occupancy[i]));
if (!attacks[s][index])
attacks[s][index] = reference[i];
else if (attacks[s][index] != reference[i])
break;
}
} while (i != size);
}
}
void init_between_bitboards() {
Square s1, s2, s3, d;
int f, r;
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
f = file_distance(s1, s2);
r = rank_distance(s1, s2);
d = (s2 - s1) / Max(f, r);
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,27 +23,6 @@
#include "types.h"
const Bitboard EmptyBoardBB = 0;
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);
extern Bitboard SquaresByColorBB[2];
extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
@@ -60,17 +39,15 @@ extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64];
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern uint64_t RMagics[64];
extern int RShifts[64];
extern Bitboard RMasks[64];
extern Bitboard* RAttacks[64];
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern uint64_t BMagics[64];
extern int BShifts[64];
extern Bitboard BMasks[64];
extern Bitboard* BAttacks[64];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
@@ -86,11 +63,11 @@ inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
}
inline void set_bit(Bitboard *b, Square s) {
inline void set_bit(Bitboard* b, Square s) {
*b |= SetMaskBB[s];
}
inline void clear_bit(Bitboard *b, Square s) {
inline void clear_bit(Bitboard* b, Square s) {
*b &= ClearMaskBB[s];
}
@@ -102,7 +79,7 @@ inline Bitboard make_move_bb(Square from, Square to) {
return SetMaskBB[from] | SetMaskBB[to];
}
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
inline void do_move_bb(Bitboard* b, Bitboard move_bb) {
*b ^= move_bb;
}
@@ -115,7 +92,7 @@ inline Bitboard rank_bb(Rank r) {
}
inline Bitboard rank_bb(Square s) {
return RankBB[square_rank(s)];
return RankBB[rank_of(s)];
}
inline Bitboard file_bb(File f) {
@@ -123,33 +100,25 @@ inline Bitboard file_bb(File f) {
}
inline Bitboard file_bb(Square s) {
return FileBB[square_file(s)];
return FileBB[file_of(s)];
}
/// neighboring_files_bb takes a file or a square as input and returns a
/// bitboard representing all squares on the neighboring files.
/// neighboring_files_bb takes a file as input and returns a bitboard representing
/// all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f];
}
inline Bitboard neighboring_files_bb(Square s) {
return NeighboringFilesBB[square_file(s)];
}
/// this_and_neighboring_files_bb takes a file or a square as input and returns
/// a bitboard representing all squares on the given and neighboring files.
/// this_and_neighboring_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and neighboring files.
inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f];
}
inline Bitboard this_and_neighboring_files_bb(Square s) {
return ThisAndNeighboringFilesBB[square_file(s)];
}
/// 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
@@ -162,7 +131,7 @@ inline Bitboard in_front_bb(Color c, Rank r) {
}
inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][square_rank(s)];
return InFrontBB[c][rank_of(s)];
}
@@ -173,32 +142,35 @@ inline Bitboard in_front_bb(Color c, Square s) {
#if defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
return unsigned(((occ & RMasks[s]) * RMagics[s]) >> RShifts[s]);
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
return unsigned(((occ & BMasks[s]) * BMagics[s]) >> BShifts[s]);
}
#else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
Bitboard b = occ & RMasks[s];
return unsigned(int(b) * int(RMagics[s]) ^ int(b >> 32) * int(RMagics[s] >> 32)) >> RShifts[s];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
Bitboard b = occ & BMasks[s];
return unsigned(int(b) * int(BMagics[s]) ^ int(b >> 32) * int(BMagics[s] >> 32)) >> BShifts[s];
}
#endif
inline Bitboard rook_attacks_bb(Square s, Bitboard occ) {
return RAttacks[s][rook_index(s, occ)];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard occ) {
return BAttacks[s][bishop_index(s, occ)];
}
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
@@ -249,7 +221,16 @@ inline Bitboard attack_span_mask(Color c, Square s) {
inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3));
& ( SetMaskBB[s1] | SetMaskBB[s2] | SetMaskBB[s3]);
}
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return bit_is_set(0xAA55AA55AA55AA55ULL, s) ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
}
@@ -290,6 +271,6 @@ extern Square pop_1st_bit(Bitboard* b);
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
extern void bitboards_init();
#endif // !defined(BITBOARD_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,25 +21,29 @@
#if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h"
enum BitCountType {
CNT64,
CNT64_MAX15,
CNT32,
CNT32_MAX15,
CNT_POPCNT
CNT_64,
CNT_64_MAX15,
CNT_32,
CNT_32_MAX15,
CNT_HW_POPCNT
};
/// count_1s() counts the number of nonzero bits in a bitboard.
/// We have different optimized versions according if platform
/// is 32 or 64 bits, and to the maximum number of nonzero bits.
/// We also support hardware popcnt instruction. See Readme.txt
/// on how to pgo compile with popcnt support.
template<BitCountType> inline int count_1s(Bitboard);
/// Determine at compile time the best popcount<> specialization according if
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// use hardware popcnt instruction when available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
/// popcount() counts the number of nonzero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<>
inline int count_1s<CNT64>(Bitboard b) {
inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -48,7 +52,7 @@ inline int count_1s<CNT64>(Bitboard b) {
}
template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) {
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL;
@@ -56,7 +60,7 @@ inline int count_1s<CNT64_MAX15>(Bitboard b) {
}
template<>
inline int count_1s<CNT32>(Bitboard b) {
inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
@@ -69,7 +73,7 @@ inline int count_1s<CNT32>(Bitboard b) {
}
template<>
inline int count_1s<CNT32_MAX15>(Bitboard b) {
inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
@@ -81,17 +85,27 @@ inline int count_1s<CNT32_MAX15>(Bitboard b) {
}
template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) {
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
assert(false);
return int(b != 0); // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return (int)__popcnt64(b);
#elif defined(__GNUC__)
#else
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
#endif
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,17 +17,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#include <algorithm>
#include <cassert>
#include <iostream>
#include "book.h"
#include "misc.h"
#include "movegen.h"
using namespace std;
@@ -35,7 +36,7 @@ using namespace std;
namespace {
// Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = {
const Key PolyGlotRandoms[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -299,227 +300,188 @@ namespace {
0xF8D626AAAF278509ULL
};
// Indices to the Random64[] array
const int PieceIdx = 0;
const int CastleIdx = 768;
const int EnPassantIdx = 772;
const int TurnIdx = 780;
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms + 0;
const Key* ZobCastle = PolyGlotRandoms + 768;
const Key* ZobEnPassant = PolyGlotRandoms + 772;
const Key* ZobTurn = PolyGlotRandoms + 780;
// book_key() builds up a PolyGlot hash key out of a position
// PieceOffset is calculated as 64 * (PolyPiece ^ 1) where PolyPiece
// is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
const int PieceOffset[] = { 0, 64, 192, 320, 448, 576, 704, 0,
0, 0, 128, 256, 384, 512, 640 };
// book_key() returns the PolyGlot hash key of the given position
uint64_t book_key(const Position& pos) {
// Piece offset is calculated as (64 * PolyPieceType + square), where
// PolyPieceType is: BP = 0, WP = 1, BN = 2, WN = 3 .... BK = 10, WK = 11
static const int PieceToPoly[] = { 0, 1, 3, 5, 7, 9, 11, 0, 0, 0, 2, 4, 6, 8, 10 };
uint64_t result = 0;
uint64_t key = 0;
Bitboard b = pos.occupied_squares();
while (b)
{
Square s = pop_1st_bit(&b);
int p = PieceToPoly[int(pos.piece_on(s))];
result ^= Random64[PieceIdx + 64 * p + int(s)];
key ^= ZobPiece[PieceOffset[pos.piece_on(s)] + s];
}
if (pos.can_castle_kingside(WHITE))
result ^= Random64[CastleIdx + 0];
b = (pos.can_castle(WHITE_OO) << 0) | (pos.can_castle(WHITE_OOO) << 1)
| (pos.can_castle(BLACK_OO) << 2) | (pos.can_castle(BLACK_OOO) << 3);
if (pos.can_castle_queenside(WHITE))
result ^= Random64[CastleIdx + 1];
if (pos.can_castle_kingside(BLACK))
result ^= Random64[CastleIdx + 2];
if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3];
while (b)
key ^= ZobCastle[pop_1st_bit(&b)];
if (pos.ep_square() != SQ_NONE)
result ^= Random64[EnPassantIdx + square_file(pos.ep_square())];
key ^= ZobEnPassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
result ^= Random64[TurnIdx];
key ^= ZobTurn[0];
return result;
return key;
}
} // namespace
Book::Book() : size(0) {
for (int i = abs(system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
Book::~Book() { if (is_open()) close(); }
/// Book::operator>>() reads sizeof(T) chars from the file's binary byte stream
/// and converts them in a number of type T. A Polyglot book stores numbers in
/// big-endian format.
template<typename T> Book& Book::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> Book& Book::operator>>(BookEntry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
}
/// Book c'tor. Make random number generation less deterministic, for book moves
Book::Book() {
/// Book::open() tries to open a book file with the given name after closing
/// any exsisting one.
for (int i = abs(get_system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>();
}
bool Book::open(const char* fName) {
fileName = "";
/// Book destructor. Be sure file is closed before we leave.
if (is_open()) // Cannot close an already closed file
close();
Book::~Book() {
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
close();
}
if (!is_open())
return false; // Silently fail if the file is not found
// Get the book size in number of entries, we are already at the end of file
size = tellg() / sizeof(BookEntry);
/// Book::close() closes the file only if it is open, otherwise
/// we can end up in a little mess due to how std::ifstream works.
void Book::close() {
if (bookFile.is_open())
bookFile.close();
bookName = "";
}
/// Book::open() opens a book file with a given file name
void Book::open(const string& fileName) {
// Close old file before opening the new
close();
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary);
// Silently return when asked to open a non-exsistent file
if (!bookFile.is_open())
return;
// Get the book size in number of entries
bookFile.seekg(0, ios::end);
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
if (!bookFile.good())
if (!good())
{
cerr << "Failed to open book file " << fileName << endl;
cerr << "Failed to open book file " << fName << endl;
exit(EXIT_FAILURE);
}
// Set only if successful
bookName = fileName;
fileName = fName; // Set only if successful
return true;
}
/// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found. If findBestMove is true then
/// return always the highest rated book move.
/// Book::probe() tries to find a book move for the given position. If no move
/// is found returns MOVE_NONE. If pickBest is true returns always the highest
/// rated move, otherwise randomly chooses one, based on the move score.
Move Book::get_move(const Position& pos, bool findBestMove) {
Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
if (!bookFile.is_open() || bookSize == 0)
return MOVE_NONE;
BookEntry entry;
int bookMove = MOVE_NONE;
unsigned score, scoresSum = 0, bestScore = 0;
BookEntry e;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position
for (int idx = find_entry(key); idx < bookSize; idx++)
if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE;
binary_search(key);
while (*this >> e, e.key == key && good())
{
entry = read_entry(idx);
best = max(best, e.count);
sum += e.count;
if (entry.key != key)
break;
score = entry.count;
if (!findBestMove)
{
// Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen.
scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move;
}
else if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
// Choose book move according to its score. If a move has a very
// high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
if ( (RKiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow "king captures rook" representation. So in case
// book move is a promotion we have to convert to our representation, in
// all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7;
// Castling moves follow "king captures rook" representation. So in case book
// move is a promotion we have to convert to our representation, in all the
// other cases we can directly compare with a Move after having masked out
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
int pt = (move >> 12) & 7;
if (pt)
move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1));
if (p)
bookMove = int(make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)), PieceType(p + 1)));
// Verify the book move (if any) is legal
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & ~(3 << 14)) == bookMove) // Mask out special flags
return cur->move;
// Add 'special move' flags and verify it is legal
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() & 0x3FFF))
return ml.move();
return MOVE_NONE;
}
/// Book::find_entry() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book
/// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
/// Book::binary_search() takes a book key as input, and does a binary search
/// through the book file for the given key. File stream current position is set
/// to the leftmost book entry with the same key as the input.
int Book::find_entry(uint64_t key) {
int left, right, mid;
// Binary search (finds the leftmost entry)
left = 0;
right = bookSize - 1;
assert(left <= right);
while (left < right)
{
mid = (left + right) / 2;
assert(mid >= left && mid < right);
if (key <= read_entry(mid).key)
right = mid;
else
left = mid + 1;
}
assert(left == right);
return read_entry(left).key == key ? left : bookSize;
}
/// Book::read_entry() takes an integer index, and returns the BookEntry
/// at the given index in the book file.
BookEntry Book::read_entry(int idx) {
assert(idx >= 0 && idx < bookSize);
assert(bookFile.is_open());
void Book::binary_search(uint64_t key) {
size_t low, high, mid;
BookEntry e;
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
low = 0;
high = size - 1;
*this >> e.key >> e.move >> e.count >> e.learn;
assert(low <= high);
if (!bookFile.good())
while (low < high && good())
{
cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE);
mid = (low + high) / 2;
assert(mid >= low && mid < high);
seekg(mid * sizeof(BookEntry), ios_base::beg);
*this >> e;
if (key <= e.key)
high = mid;
else
low = mid + 1;
}
return e;
assert(low == high);
seekg(low * sizeof(BookEntry), ios_base::beg);
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,14 +23,13 @@
#include <fstream>
#include <string>
#include "move.h"
#include "position.h"
#include "rkiss.h"
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored highest byte first (regardless of size). The entries are ordered
// according to key. Lowest key first.
/// A Polyglot book is a series of "entries" of 16 bytes. All integers are
/// stored highest byte first (regardless of size). The entries are ordered
/// according to key. Lowest key first.
struct BookEntry {
uint64_t key;
uint16_t move;
@@ -38,34 +37,22 @@ struct BookEntry {
uint32_t learn;
};
class Book {
class Book : private std::ifstream {
public:
Book();
~Book();
void open(const std::string& fileName);
void close();
Move get_move(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
Move probe(const Position& pos, const std::string& fName, bool pickBest);
private:
// read n chars from the file stream and converts them in an
// integer number. Integers are stored with highest byte first.
template<int n> uint64_t get_int();
template<typename T> Book& operator>>(T& n);
template<typename T>
Book& operator>>(T& n) { n = (T)get_int<sizeof(T)>(); return *this; }
bool open(const char* fName);
void binary_search(uint64_t key);
BookEntry read_entry(int idx);
int find_entry(uint64_t key);
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS RKiss;
std::string fileName;
size_t size;
};
// Yes, we indulge a bit here ;-)
template<int n> inline uint64_t Book::get_int() { return 256 * get_int<n-1>() + bookFile.get(); }
template<> inline uint64_t Book::get_int<1>() { return bookFile.get(); }
#endif // !defined(BOOK_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitcount.h"
@@ -59,107 +60,75 @@ namespace {
// the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Get the material key of a Position out of the given endgame key code
// like "KBPKN". The trick here is to first forge an ad-hoc fen string
// and then let a Position object to do the work for us. Note that the
// fen string could correspond to an illegal position.
Key key(const string& code, Color c) {
// Build corresponding key code for the opposite color: "KBPKN" -> "KNKBP"
const string swap_colors(const string& keyCode) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx);
string sides[] = { code.substr(code.find('K', 1)), // Weaker
code.substr(0, code.find('K', 1)) }; // Stronger
transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fen = sides[0] + char('0' + int(8 - code.length()))
+ sides[1] + "/8/8/8/8/8/8/8 w - - 0 10";
return Position(fen, false, 0).material_key();
}
// Get the material key of a position out of the given endgame key code
// like "KBPKN". The trick here is to first build up a FEN string and then
// let a Position object to do the work for us. Note that the FEN string
// could correspond to an illegal position.
Key mat_key(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode[0] == 'K');
string fen;
size_t i = 0;
// First add white and then black pieces
do fen += keyCode[i]; while (keyCode[++i] != 'K');
do fen += char(tolower(keyCode[i])); while (++i < keyCode.length());
// Add file padding and remaining empty ranks
fen += string(1, '0' + int(8 - keyCode.length())) + "/8/8/8/8/8/8/8 w - -";
// Build a Position out of the fen string and get its material key
return Position(fen, false, 0).get_material_key();
}
typedef EndgameBase<Value> EF;
typedef EndgameBase<ScaleFactor> SF;
template<typename M>
void delete_endgame(const typename M::value_type& p) { delete p.second; }
} // namespace
/// Endgames member definitions
template<> const Endgames::EFMap& Endgames::get<EF>() const { return maps.first; }
template<> const Endgames::SFMap& Endgames::get<SF>() const { return maps.second; }
/// Endgames members definitions
Endgames::Endgames() {
add<Endgame<Value, KNNK> >("KNNK");
add<Endgame<Value, KPK> >("KPK");
add<Endgame<Value, KBNK> >("KBNK");
add<Endgame<Value, KRKP> >("KRKP");
add<Endgame<Value, KRKB> >("KRKB");
add<Endgame<Value, KRKN> >("KRKN");
add<Endgame<Value, KQKR> >("KQKR");
add<Endgame<Value, KBBKN> >("KBBKN");
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKR>("KQKR");
add<KBBKN>("KBBKN");
add<Endgame<ScaleFactor, KNPK> >("KNPK");
add<Endgame<ScaleFactor, KRPKR> >("KRPKR");
add<Endgame<ScaleFactor, KBPKB> >("KBPKB");
add<Endgame<ScaleFactor, KBPPKB> >("KBPPKB");
add<Endgame<ScaleFactor, KBPKN> >("KBPKN");
add<Endgame<ScaleFactor, KRPPKRP> >("KRPPKRP");
add<KNPK>("KNPK");
add<KRPKR>("KRPKR");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
Endgames::~Endgames() {
for (EFMap::const_iterator it = get<EF>().begin(); it != get<EF>().end(); ++it)
delete it->second;
for (SFMap::const_iterator it = get<SF>().begin(); it != get<SF>().end(); ++it)
delete it->second;
for_each(m1.begin(), m1.end(), delete_endgame<M1>);
for_each(m2.begin(), m2.end(), delete_endgame<M2>);
}
template<class T>
void Endgames::add(const string& keyCode) {
template<EndgameType E>
void Endgames::add(const string& code) {
typedef typename T::Base F;
typedef std::map<Key, F*> M;
typedef typename eg_family<E>::type T;
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(swap_colors(keyCode)), new T(BLACK)));
map((T*)0)[key(code, WHITE)] = new Endgame<E>(WHITE);
map((T*)0)[key(code, BLACK)] = new Endgame<E>(BLACK);
}
template<class T>
T* Endgames::get(Key key) const {
typename std::map<Key, T*>::const_iterator it = get<T>().find(key);
return it != get<T>().end() ? it->second : NULL;
}
// Explicit template instantiations
template EF* Endgames::get<EF>(Key key) const;
template SF* Endgames::get<SF>(Key key) const;
/// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small.
template<>
Value Endgame<Value, KXK>::apply(const Position& pos) const {
Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
@@ -185,7 +154,7 @@ Value Endgame<Value, KXK>::apply(const Position& pos) const {
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color.
template<>
Value Endgame<Value, KBNK>::apply(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);
@@ -196,15 +165,15 @@ Value Endgame<Value, KBNK>::apply(const Position& pos) const {
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1.
if (opposite_color_squares(bishopSquare, SQ_A1))
if (opposite_colors(bishopSquare, SQ_A1))
{
winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq);
winnerKSq = mirror(winnerKSq);
loserKSq = mirror(loserKSq);
}
Value result = VALUE_KNOWN_WIN
@@ -217,7 +186,7 @@ Value Endgame<Value, KBNK>::apply(const Position& pos) const {
/// KP vs K. This endgame is evaluated with the help of a bitbase.
template<>
Value Endgame<Value, KPK>::apply(const Position& pos) const {
Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
@@ -231,22 +200,22 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move();
}
else
{
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0));
stm = opposite_color(pos.side_to_move());
wksq = flip(pos.king_square(BLACK));
bksq = flip(pos.king_square(WHITE));
wpsq = flip(pos.piece_list(BLACK, PAWN)[0]);
stm = flip(pos.side_to_move());
}
if (square_file(wpsq) >= FILE_E)
if (file_of(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
wksq = mirror(wksq);
bksq = mirror(bksq);
wpsq = mirror(wpsq);
}
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
@@ -254,7 +223,7 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
Value result = VALUE_KNOWN_WIN
+ PawnValueEndgame
+ Value(square_rank(wpsq));
+ Value(rank_of(wpsq));
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -265,7 +234,7 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
/// far advanced with support of the king, while the attacking king is far
/// away.
template<>
Value Endgame<Value, KRKP>::apply(const Position& pos) const {
Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -276,23 +245,23 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK, 0);
wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN, 0);
bpsq = pos.piece_list(weakerSide, PAWN)[0];
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
bksq = flip_square(bksq);
bpsq = flip_square(bpsq);
wksq = flip(wksq);
wrsq = flip(wrsq);
bksq = flip(bksq);
bpsq = flip(bpsq);
}
Square queeningSq = make_square(square_file(bpsq), RANK_1);
Square queeningSq = make_square(file_of(bpsq), RANK_1);
Value result;
// If the stronger side's king is in front of the pawn, it's a win
if (wksq < bpsq && square_file(wksq) == square_file(bpsq))
if (wksq < bpsq && file_of(wksq) == file_of(bpsq))
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the weaker side's king is too far from the pawn and the rook,
@@ -303,9 +272,9 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
else if ( square_rank(bksq) <= RANK_3
else if ( rank_of(bksq) <= RANK_3
&& square_distance(bksq, bpsq) == 1
&& square_rank(wksq) >= RANK_4
&& rank_of(wksq) >= RANK_4
&& square_distance(wksq, bpsq) - tempo > 2)
result = Value(80 - square_distance(wksq, bpsq) * 8);
@@ -322,7 +291,7 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
/// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge.
template<>
Value Endgame<Value, KRKB>::apply(const Position& pos) const {
Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -338,7 +307,7 @@ Value Endgame<Value, KRKB>::apply(const Position& pos) const {
/// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart.
template<>
Value Endgame<Value, KRKN>::apply(const Position& pos) const {
Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -346,14 +315,11 @@ Value Endgame<Value, KRKN>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
int d = square_distance(defendingKSq, nSq);
Value result = Value(10)
+ MateTable[defendingKSq]
+ KRKNKingKnightDistancePenalty[d];
const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0];
Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]);
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -364,7 +330,7 @@ Value Endgame<Value, KRKN>::apply(const Position& pos) const {
/// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR.
template<>
Value Endgame<Value, KQKR>::apply(const Position& pos) const {
Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -383,18 +349,18 @@ Value Endgame<Value, KQKR>::apply(const Position& pos) const {
}
template<>
Value Endgame<Value, KBBKN>::apply(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*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pieces(PAWN) == EmptyBoardBB);
assert(!pos.pieces(PAWN));
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0);
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
// Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]);
@@ -403,7 +369,7 @@ Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility
result += Value((8 - count_1s<CNT32_MAX15>(pos.attacks_from<KNIGHT>(nsq))) * 8);
result += Value((8 - popcount<Max15>(pos.attacks_from<KNIGHT>(nsq))) * 8);
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -412,22 +378,21 @@ Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
/// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw.
template<>
Value Endgame<Value, KmmKm>::apply(const Position&) const {
Value Endgame<KmmKm>::operator()(const Position&) const {
return VALUE_DRAW;
}
template<>
Value Endgame<Value, KNNK>::apply(const Position&) const {
Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// 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
/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
template<>
ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -437,18 +402,18 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(PAWN, strongerSide);
File pawnFile = square_file(pos.piece_list(strongerSide, PAWN, 0));
File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]);
// All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& (pawns & ~file_bb(pawnFile)) == EmptyBoardBB)
&& !(pawns & ~file_bb(pawnFile)))
{
Square bishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide);
if ( opposite_color_squares(queeningSq, bishopSq)
&& abs(square_file(kingSq) - pawnFile) <= 1)
if ( opposite_colors(queeningSq, bishopSq)
&& abs(file_of(kingSq) - pawnFile) <= 1)
{
// The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the
@@ -456,12 +421,12 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
Rank rank;
if (strongerSide == WHITE)
{
for (rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--) {}
for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {}
assert(rank >= RANK_2 && rank <= RANK_7);
}
else
{
for (rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {}
for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {}
rank = Rank(rank ^ 7); // HACK to get the relative rank
assert(rank >= RANK_2 && rank <= RANK_7);
}
@@ -469,19 +434,17 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
// is placed somewhere in front of the pawn, it's a draw.
if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
}
return SCALE_FACTOR_NONE;
}
/// KQKRPScalingFunction scales endgames where the stronger side has only
/// king and queen, while the weaker side has at least a rook and a pawn.
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
/// K and queen vs K, rook and one or more pawns. It tests for fortress draws with
/// a rook on the third rank defended by a pawn.
template<>
ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const {
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1);
@@ -496,23 +459,22 @@ ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const {
&& (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
{
Square rsq = pos.piece_list(weakerSide, ROOK, 0);
Square rsq = pos.piece_list(weakerSide, ROOK)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
}
/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a
/// handful of the most important classes of drawn positions, but is far
/// from perfect. It would probably be a good idea to add more knowledge
/// in the future.
/// K, rook and one pawn vs K and a rook. This function knows a handful of the
/// most important classes of drawn positions, but is far from perfect. It would
/// probably be a good idea to add more knowledge in the future.
///
/// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty.
template<>
ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1);
@@ -520,32 +482,32 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK, 0);
Square wpsq = pos.piece_list(strongerSide, PAWN, 0);
Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK, 0);
Square brsq = pos.piece_list(weakerSide, ROOK)[0];
// Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board.
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
wpsq = flip_square(wpsq);
bksq = flip_square(bksq);
brsq = flip_square(brsq);
wksq = flip(wksq);
wrsq = flip(wrsq);
wpsq = flip(wpsq);
bksq = flip(bksq);
brsq = flip(brsq);
}
if (square_file(wpsq) > FILE_D)
if (file_of(wpsq) > FILE_D)
{
wksq = flop_square(wksq);
wrsq = flop_square(wrsq);
wpsq = flop_square(wpsq);
bksq = flop_square(bksq);
brsq = flop_square(brsq);
wksq = mirror(wksq);
wrsq = mirror(wrsq);
wpsq = mirror(wpsq);
bksq = mirror(bksq);
brsq = mirror(brsq);
}
File f = square_file(wpsq);
Rank r = square_rank(wpsq);
File f = file_of(wpsq);
Rank r = rank_of(wpsq);
Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongerSide);
@@ -554,31 +516,31 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
if ( r <= RANK_5
&& square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6)))
return SCALE_FACTOR_ZERO;
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( r == RANK_6
&& square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3)))
return SCALE_FACTOR_ZERO;
&& rank_of(wksq) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3)))
return SCALE_FACTOR_DRAW;
if ( r >= RANK_6
&& bksq == queeningSq
&& square_rank(brsq) == RANK_1
&& rank_of(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn.
if ( wpsq == SQ_A7
&& wrsq == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5))
return SCALE_FACTOR_ZERO;
&& file_of(brsq) == FILE_A
&& (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5))
return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
@@ -586,14 +548,14 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
&& bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
// Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7
&& f != FILE_A
&& square_file(wrsq) == f
&& file_of(wrsq) == f
&& wrsq != queeningSq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo))
@@ -601,7 +563,7 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
// Similar to the above, but with the pawn further back
if ( f != FILE_A
&& square_file(wrsq) == f
&& file_of(wrsq) == f
&& wrsq < wpsq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
@@ -616,9 +578,9 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
// the pawn's path, it's probably a draw.
if (r <= RANK_4 && bksq > wpsq)
{
if (square_file(bksq) == square_file(wpsq))
if (file_of(bksq) == file_of(wpsq))
return ScaleFactor(10);
if ( abs(square_file(bksq) - square_file(wpsq)) == 1
if ( abs(file_of(bksq) - file_of(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
}
@@ -626,19 +588,19 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
}
/// KRPPKRPScalingFunction scales KRPP vs KRP endgames. There is only a
/// single pattern: If the stronger side has no pawns and the defending king
/// K, rook and two pawns vs K, rook and one pawn. There is only a single
/// pattern: If the stronger side has no passed pawns and the defending king
/// is actively placed, the position is drawish.
template<>
ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN, 0);
Square wpsq2 = pos.piece_list(strongerSide, PAWN, 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1];
Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn?
@@ -646,7 +608,7 @@ ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
|| pos.pawn_is_passed(strongerSide, wpsq2))
return SCALE_FACTOR_NONE;
Rank r = Max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));
Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));
if ( file_distance(bksq, wpsq1) <= 1
&& file_distance(bksq, wpsq2) <= 1
@@ -665,11 +627,10 @@ ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
}
/// KPsKScalingFunction scales endgames with king and two or more pawns
/// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw.
/// K and two or more pawns vs K. There is just a single rule here: If all pawns
/// are on the same rook file and are blocked by the defending king, it's a draw.
template<>
ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2);
@@ -680,34 +641,33 @@ ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB)
if (!(pawns & ~FileABB))
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( square_file(ksq) == FILE_A
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
|| ( file_of(ksq) == FILE_A
&& !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_DRAW;
}
// Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB)
else if (!(pawns & ~FileHBB))
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( square_file(ksq) == FILE_H
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
|| ( file_of(ksq) == FILE_H
&& !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
}
/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw.
/// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending
/// king is somewhere along the path of the pawn, and the square of the king is
/// not of the same color as the stronger side's bishop, it's a draw. If the two
/// bishops have opposite color, it's almost always a draw.
template<>
ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -716,20 +676,20 @@ ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP, 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq)
if ( file_of(weakerKingSq) == file_of(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
// Case 2: Opposite colored bishops
if (opposite_color_squares(strongerBishopSq, weakerBishopSq))
if (opposite_colors(strongerBishopSq, weakerBishopSq))
{
// We assume that the position is drawn in the following three situations:
//
@@ -742,27 +702,27 @@ ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
// reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else
{
Bitboard path = squares_in_front_of(strongerSide, pawnSq);
if (path & pos.pieces(KING, weakerSide))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
}
return SCALE_FACTOR_NONE;
}
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops.
/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with
/// opposite-colored bishops.
template<>
ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -771,28 +731,28 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP)[0];
Square bbsq = pos.piece_list(weakerSide, BISHOP)[0];
if (!opposite_color_squares(wbsq, bbsq))
if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN, 0);
Square psq2 = pos.piece_list(strongerSide, PAWN, 1);
Rank r1 = square_rank(psq1);
Rank r2 = square_rank(psq2);
Square psq1 = pos.piece_list(strongerSide, PAWN)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2;
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
{
blockSq1 = psq1 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq2), square_rank(psq1));
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
}
else
{
blockSq1 = psq2 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq1), square_rank(psq2));
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
}
switch (file_distance(psq1, psq2))
@@ -800,10 +760,10 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
case 0:
// Both pawns are on the same file. Easy draw if defender firmly controls
// some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1)
if ( file_of(ksq) == file_of(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& opposite_color_squares(ksq, wbsq))
return SCALE_FACTOR_ZERO;
&& opposite_colors(ksq, wbsq))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
@@ -812,17 +772,17 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
// in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn.
if ( ksq == blockSq1
&& opposite_color_squares(ksq, wbsq)
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| abs(r1 - r2) >= 2))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_color_squares(ksq, wbsq)
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
@@ -833,12 +793,11 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
}
/// KBPKNScalingFunction scales KBP vs KN endgames. There is a single rule:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw.
/// K, bisop and a pawn vs K and knight. There is a single rule: If the defending
/// king is somewhere along the path of the pawn, and the square of the king is
/// not of the same color as the stronger side's bishop, it's a draw.
template<>
ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const {
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -847,25 +806,25 @@ ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide);
if ( square_file(weakerKingSq) == square_file(pawnSq)
if ( file_of(weakerKingSq) == file_of(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule:
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn.
/// K, knight and a pawn vs K. There is a single rule: If the pawn is a rook pawn
/// on the 7th rank and the defending king prevents the pawn from advancing, the
/// position is drawn.
template<>
ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const {
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
@@ -873,67 +832,61 @@ ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing
/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest
/// side has a draw without the pawn, she probably has at least a draw with
/// the pawn as well. The exception is when the stronger side's pawn is far
/// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
/// the pawn, she probably has at least a draw with the pawn as well. The exception
/// is when the stronger side's pawn is far advanced and not on a rook file; in
/// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<>
ScaleFactor Endgame<ScaleFactor, KPKP>::apply(const Position& pos) const {
ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1);
Square wksq, bksq, wpsq;
Color stm;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Color stm = pos.side_to_move();
if (strongerSide == WHITE)
if (strongerSide == BLACK)
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
stm = pos.side_to_move();
}
else
{
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0));
stm = opposite_color(pos.side_to_move());
wksq = flip(wksq);
bksq = flip(bksq);
wpsq = flip(wpsq);
stm = flip(stm);
}
if (square_file(wpsq) >= FILE_E)
if (file_of(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
wksq = mirror(wksq);
bksq = mirror(bksq);
wpsq = mirror(wpsq);
}
// If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw.
if ( square_rank(wpsq) >= RANK_5
&& square_file(wpsq) != FILE_A)
if ( rank_of(wpsq) >= RANK_5
&& file_of(wpsq) != FILE_A)
return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, it's probably at least a draw even with the pawn.
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
#if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED
#include <string>
#include <map>
#include <string>
#include "position.h"
#include "types.h"
@@ -46,6 +46,7 @@ enum EndgameType {
// Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns
@@ -60,25 +61,30 @@ enum EndgameType {
};
/// Some magic to detect family type of endgame from its enum value
template<bool> struct bool_to_type { typedef Value type; };
template<> struct bool_to_type<true> { typedef ScaleFactor type; };
template<EndgameType E> struct eg_family : public bool_to_type<(E > SCALE_FUNS)> {};
/// Base and derived templates for endgame evaluation and scaling functions
template<typename T>
struct EndgameBase {
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {}
virtual Color color() const = 0;
virtual T apply(const Position&) const = 0;
virtual T operator()(const Position&) const = 0;
};
template<typename T, EndgameType>
template<EndgameType E, typename T = typename eg_family<E>::type>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {}
Color color() const { return strongerSide; }
T apply(const Position&) const;
T operator()(const Position&) const;
private:
Color strongerSide, weakerSide;
@@ -87,26 +93,28 @@ private:
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its apply() method that is virtual.
/// endgame function calling its operator() method that is virtual.
class Endgames {
typedef std::map<Key, EndgameBase<Value>* > EFMap;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap;
typedef std::map<Key, EndgameBase<Value>*> M1;
typedef std::map<Key, EndgameBase<ScaleFactor>*> M2;
M1 m1;
M2 m2;
M1& map(Value*) { return m1; }
M2& map(ScaleFactor*) { return m2; }
template<EndgameType E> void add(const std::string& code);
public:
Endgames();
~Endgames();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const std::string& keyCode);
// Here we store two maps, for evaluate and scaling functions...
std::pair<EFMap, SFMap> maps;
// ...and here is the accessing template function
template<typename T> const std::map<Key, T*>& get() const;
template<typename T> EndgameBase<T>* get(Key key) {
return map((T*)0).count(key) ? map((T*)0)[key] : NULL;
}
};
#endif // !defined(ENDGAME_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <iostream>
#include <iomanip>
#include <sstream>
#include <algorithm>
#include "bitcount.h"
#include "evaluate.h"
@@ -44,20 +45,20 @@ namespace {
// all squares attacked by the given color.
Bitboard attackedBy[2][8];
// kingZone[color] is the zone around the enemy king which is considered
// kingRing[color] is the zone around the king which is considered
// by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file)
// squares two ranks in front of the king. For instance, if black's king
// is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8,
// is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6.
Bitboard kingZone[2];
Bitboard kingRing[2];
// kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingZone of the enemy king.
// which attack a square in the kingRing of the enemy king.
int kingAttackersCount[2];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingZone of the enemy king. The
// given color which attack a square in the kingRing of the enemy king. The
// weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
@@ -88,7 +89,7 @@ namespace {
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(248, 271), S(252, 259), S(46, 0), S(247, 0), S(259, 0)
S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0)
};
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
@@ -142,9 +143,9 @@ namespace {
{ S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
};
// ThreatedByPawnPenalty[PieceType] contains a penalty according to which
// ThreatenedByPawnPenalty[PieceType] contains a penalty according to which
// piece type is attacked by an enemy pawn.
const Score ThreatedByPawnPenalty[] = {
const Score ThreatenedByPawnPenalty[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
};
@@ -224,28 +225,27 @@ namespace {
};
// Function prototypes
template<bool HasPopCnt, bool Trace>
template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin);
template<Color Us, bool HasPopCnt>
template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt>
template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight);
@@ -260,22 +260,17 @@ namespace {
/// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) {
return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
: do_evaluate<false, false>(pos, margin);
}
Value evaluate(const Position& pos, Value& margin) { return do_evaluate<false>(pos, margin); }
namespace {
template<bool HasPopCnt, bool Trace>
template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei;
Value margins[2];
Score score, mobilityWhite, mobilityBlack;
assert(pos.is_ok());
assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.in_check());
@@ -288,7 +283,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Probe the material hash table
ei.mi = Threads[pos.thread()].materialTable.get_material_info(pos);
ei.mi = Threads[pos.thread()].materialTable.material_info(pos);
score += ei.mi->material_value();
// If we have a specialized evaluation function for the current material
@@ -300,23 +295,23 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// Probe the pawn hash table
ei.pi = Threads[pos.thread()].pawnTable.get_pawn_info(pos);
ei.pi = Threads[pos.thread()].pawnTable.pawn_info(pos);
score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards
init_eval_info<WHITE, HasPopCnt>(pos, ei);
init_eval_info<BLACK, HasPopCnt>(pos, ei);
init_eval_info<WHITE>(pos, ei);
init_eval_info<BLACK>(pos, ei);
// Evaluate pieces and mobility
score += evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack);
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack);
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation.
score += evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins);
score += evaluate_king<WHITE, Trace>(pos, ei, margins)
- evaluate_king<BLACK, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE>(pos, ei)
@@ -328,12 +323,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
// 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))
score += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
score += evaluate_unstoppable_pawns(pos, ei);
// Evaluate space for both sides, only in middle-game.
if (ei.mi->space_weight())
{
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(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]);
}
@@ -375,9 +370,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns<false>(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE, false>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK, false>(pos, ei), 0);
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei), 0);
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])
@@ -412,7 +407,7 @@ void read_evaluation_uci_options(Color us) {
// If running in analysis mode, make sure we use symmetrical king safety. We do this
// by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (Options["UCI_AnalyseMode"].value<bool>())
if (Options["UCI_AnalyseMode"])
Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
init_safety();
@@ -424,10 +419,9 @@ namespace {
// init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation.
template<Color Us, bool HasPopCnt>
template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
@@ -437,12 +431,12 @@ namespace {
if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
{
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : 0;
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
} else
ei.kingZone[Us] = ei.kingAttackersCount[Us] = 0;
ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
}
@@ -462,8 +456,8 @@ namespace {
// no minor piece which can exchange the outpost piece.
if (bonus && bit_is_set(ei.attackedBy[Us][PAWN], s))
{
if ( pos.pieces(KNIGHT, Them) == EmptyBoardBB
&& (SquaresByColorBB[square_color(s)] & pos.pieces(BISHOP, Them)) == EmptyBoardBB)
if ( !pos.pieces(KNIGHT, Them)
&& !(same_color_squares(s) & pos.pieces(BISHOP, Them)))
bonus += bonus + bonus / 2;
else
bonus += bonus / 2;
@@ -474,7 +468,7 @@ namespace {
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
template<PieceType Piece, Color Us, bool HasPopCnt, bool Trace>
template<PieceType Piece, Color Us, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b;
@@ -483,14 +477,12 @@ namespace {
File f;
Score score = SCORE_ZERO;
const BitCountType Full = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64 : CNT32;
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* ptr = pos.piece_list_begin(Us, Piece);
const Square* pl = pos.piece_list(Us, Piece);
ei.attackedBy[Us][Piece] = EmptyBoardBB;
ei.attackedBy[Us][Piece] = 0;
while ((s = *ptr++) != SQ_NONE)
while ((s = *pl++) != SQ_NONE)
{
// Find attacked squares, including x-ray attacks for bishops and rooks
if (Piece == KNIGHT || Piece == QUEEN)
@@ -506,28 +498,29 @@ namespace {
ei.attackedBy[Us][Piece] |= b;
// King attacks
if (b & ei.kingZone[Us])
if (b & ei.kingRing[Them])
{
ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]);
if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += count_1s<Max15>(bb);
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
}
// Mobility
mob = (Piece != QUEEN ? count_1s<Max15>(b & mobilityArea)
: count_1s<Full >(b & mobilityArea));
mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea));
mobility += MobilityBonus[Piece][mob];
// 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.
if (bit_is_set(ei.attackedBy[Them][PAWN], s))
score -= ThreatedByPawnPenalty[Piece];
score -= ThreatenedByPawnPenalty[Piece];
// Bishop and knight outposts squares
if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Us))
if ( (Piece == BISHOP || Piece == KNIGHT)
&& !(pos.pieces(PAWN, Them) & attack_span_mask(Us, s)))
score += evaluate_outposts<Piece, Us>(pos, ei, s);
// Queen or rook on 7th rank
@@ -546,7 +539,7 @@ namespace {
// 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) + (square_file(s) == FILE_A ? DELTA_E : DELTA_W);
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.square_is_empty(s + d + pawn_push(Us)))
@@ -563,7 +556,7 @@ namespace {
if (Piece == ROOK)
{
// Open and half-open files
f = square_file(s);
f = file_of(s);
if (ei.pi->file_is_half_open(Us, f))
{
if (ei.pi->file_is_half_open(Them, f))
@@ -579,21 +572,21 @@ namespace {
ksq = pos.king_square(Us);
if ( square_file(ksq) >= FILE_E
&& square_file(s) > square_file(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s)))
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, square_file(ksq)))
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 ( square_file(ksq) <= FILE_D
&& square_file(s) < square_file(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s)))
else if ( file_of(ksq) <= FILE_D
&& 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_left(Us, square_file(ksq)))
if (!ei.pi->has_open_file_to_left(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
@@ -619,7 +612,7 @@ namespace {
Score score = SCORE_ZERO;
// Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies = pos.pieces_of_color(Them)
Bitboard weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][0];
if (!weakEnemies)
@@ -643,7 +636,7 @@ namespace {
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// pieces of a given color.
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -651,12 +644,12 @@ namespace {
Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us));
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us));
score += evaluate_pieces<KNIGHT, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<ROOK, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, HasPopCnt, 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<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
@@ -668,10 +661,9 @@ namespace {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard undefended, b, b1, b2, safe;
@@ -698,14 +690,14 @@ namespace {
// the number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter.
attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s<Max15>(undefended))
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)]
- mg_value(ei.pi->king_shelter<Us>(pos, ksq)) / 32;
// Analyse enemy's safe queen contact checks. First find undefended
// squares around the king attacked by enemy queen...
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces_of_color(Them);
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
if (b)
{
// ...then remove squares not supported by another enemy piece
@@ -713,13 +705,13 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b)
attackUnits += QueenContactCheckBonus
* count_1s<Max15>(b)
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
// Analyse enemy's safe rook contact checks. First find undefended
// squares around the king attacked by enemy rooks...
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces_of_color(Them);
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy rook gives check
b &= RookPseudoAttacks[ksq];
@@ -731,12 +723,12 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheckBonus
* count_1s<Max15>(b)
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
// Analyse enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces_of_color(Them) | ei.attackedBy[Us][0]);
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]);
b1 = pos.attacks_from<ROOK>(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
@@ -744,25 +736,25 @@ namespace {
// Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b)
attackUnits += QueenCheckBonus * count_1s<Max15>(b);
attackUnits += QueenCheckBonus * popcount<Max15>(b);
// Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK];
if (b)
attackUnits += RookCheckBonus * count_1s<Max15>(b);
attackUnits += RookCheckBonus * popcount<Max15>(b);
// Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP];
if (b)
attackUnits += BishopCheckBonus * count_1s<Max15>(b);
attackUnits += BishopCheckBonus * popcount<Max15>(b);
// Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b)
attackUnits += KnightCheckBonus * count_1s<Max15>(b);
attackUnits += KnightCheckBonus * popcount<Max15>(b);
// To index KingDangerTable[] attackUnits must be in [0, 99] range
attackUnits = Min(99, Max(0, attackUnits));
attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDangerTable[]
// array and subtract the score from evaluation. Set also margins[]
@@ -812,11 +804,11 @@ namespace {
Square blockSq = s + pawn_push(Us);
// Adjust bonus based on kings proximity
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 6 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * rr);
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr);
// If blockSq is not the queening square then consider also a second push
if (square_rank(blockSq) != (Us == WHITE ? RANK_8 : RANK_1))
if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1))
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
// If the pawn is free to advance, increase bonus
@@ -832,7 +824,7 @@ namespace {
&& (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces_of_color(Them));
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them));
// If there aren't enemy attacks or pieces along the path to queen give
// huge bonus. Even bigger if we protect the pawn's path.
@@ -844,19 +836,15 @@ namespace {
// If yes, big bonus (but smaller than when there are no enemy attacks),
// if no, somewhat smaller bonus.
ebonus += Value(rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8));
// At last, add a small bonus when there are no *friendly* pieces
// in the pawn's path.
if (!(squaresToQueen & pos.pieces_of_color(Us)))
ebonus += Value(rr);
}
} // rr != 0
// Increase the bonus if the passed pawn is supported by a friendly pawn
// on the same rank and a bit smaller if it's on the previous rank.
supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(s);
supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(file_of(s));
if (supportingPawns & rank_bb(s))
ebonus += Value(r * 20);
else if (supportingPawns & rank_bb(s - pawn_push(Us)))
ebonus += Value(r * 12);
@@ -866,7 +854,7 @@ namespace {
// we try the following: Increase the value for rook pawns if the
// other side has no pieces apart from a knight, and decrease the
// value if the other side has a rook or queen.
if (square_file(s) == FILE_A || square_file(s) == FILE_H)
if (file_of(s) == FILE_A || file_of(s) == FILE_H)
{
if (pos.non_pawn_material(Them) <= KnightValueMidgame)
ebonus += ebonus / 4;
@@ -885,11 +873,8 @@ namespace {
// 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.
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
Bitboard b, b2, blockers, supporters, queeningPath, candidates;
Square s, blockSq, queeningSquare;
Color c, winnerSide, loserSide;
@@ -902,7 +887,7 @@ namespace {
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c)))
if (pos.non_pawn_material(flip(c)))
continue;
b = ei.pi->passed_pawns(c);
@@ -910,12 +895,12 @@ namespace {
while (b)
{
s = pop_1st_bit(&b);
queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
queeningSquare = relative_square(c, make_square(file_of(s), RANK_8));
queeningPath = squares_in_front_of(c, s);
// Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
oppMovesToGo = square_distance(pos.king_square(flip(c)), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended)
@@ -924,16 +909,16 @@ namespace {
// Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers.
assert(!pos.in_check());
assert(queeningPath & pos.occupied_squares() == queeningPath & pos.pieces_of_color(c));
assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition
movesToGo += count_1s<Max15>(queeningPath & pos.pieces_of_color(c));
movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
pliesToGo = 2 * movesToGo - int(c == pos.side_to_move());
pliesToQueen[c] = Min(pliesToQueen[c], pliesToGo);
pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo);
}
}
@@ -943,7 +928,7 @@ namespace {
return SCORE_ZERO;
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
loserSide = opposite_color(winnerSide);
loserSide = flip(winnerSide);
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
b = candidates = pos.pieces(PAWN, loserSide);
@@ -953,7 +938,7 @@ namespace {
s = pop_1st_bit(&b);
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8));
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
@@ -977,12 +962,12 @@ namespace {
minKingDist = kingptg = 256;
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8));
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Generate list of blocking pawns and supporters
supporters = neighboring_files_bb(s) & candidates;
supporters = neighboring_files_bb(file_of(s)) & candidates;
opposed = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide);
@@ -1003,7 +988,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d);
movesToGo = std::min(movesToGo, d);
}
}
@@ -1013,7 +998,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d);
movesToGo = std::min(movesToGo, d);
}
// If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
@@ -1027,7 +1012,7 @@ namespace {
// Plies needed for the king to capture all the blocking pawns
d = square_distance(pos.king_square(loserSide), blockSq);
minKingDist = Min(minKingDist, d);
minKingDist = std::min(minKingDist, d);
kingptg = (minKingDist + blockersCount) * 2;
}
@@ -1052,10 +1037,9 @@ namespace {
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. The aim is to improve play on game opening.
template<Color Us, bool HasPopCnt>
template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
// Find the safe squares for our pieces inside the area defined by
@@ -1071,7 +1055,7 @@ namespace {
behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
return count_1s<Max15>(safe) + count_1s<Max15>(behind & safe);
return popcount<Max15>(safe) + popcount<Max15>(behind & safe);
}
@@ -1104,8 +1088,8 @@ namespace {
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
// Scale option value from 100 to 256
int mg = Options[mgOpt].value<int>() * 256 / 100;
int eg = Options[egOpt].value<int>() * 256 / 100;
int mg = Options[mgOpt] * 256 / 100;
int eg = Options[egOpt] * 256 / 100;
return apply_weight(make_score(mg, eg), internalWeight);
}
@@ -1126,9 +1110,9 @@ namespace {
t[i] = Value(int(0.4 * i * i));
if (i > 0)
t[i] = Min(t[i], t[i - 1] + MaxSlope);
t[i] = std::min(t[i], t[i - 1] + MaxSlope);
t[i] = Min(t[i], Peak);
t[i] = std::min(t[i], Peak);
}
// Then apply the weights and get the final KingDangerTable[] array
@@ -1190,7 +1174,7 @@ std::string trace_evaluate(const Position& pos) {
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<false, true>(pos, margin);
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,9 @@
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
#include <cstring>
#include "types.h"
#include <cstring>
#include <algorithm>
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
@@ -35,7 +36,7 @@ class History {
public:
void clear();
Value value(Piece p, Square to) const;
void update(Piece p, Square to, Value bonus);
void add(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
@@ -55,7 +56,7 @@ inline Value History::value(Piece p, Square to) const {
return history[p][to];
}
inline void History::update(Piece p, Square to, Value bonus) {
inline void History::add(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
}
@@ -64,7 +65,7 @@ inline Value History::gain(Piece p, Square to) const {
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = Max(g, maxGains[p][to] - 1);
maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,12 +35,15 @@ typedef pthread_cond_t WaitCondition;
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
#else
#define NOMINMAX // disable macros min() and max()
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
@@ -55,7 +58,8 @@ typedef CONDITION_VARIABLE WaitCondition;
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x, y, INFINITE,0)
# define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0)
# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
@@ -72,6 +76,8 @@ typedef HANDLE WaitCondition;
# define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
#endif
#endif

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,71 +17,41 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
#include <cstdio>
#include <iostream>
#include <string>
#include "bitboard.h"
#include "evaluate.h"
#include "misc.h"
#include "position.h"
#include "thread.h"
#include "search.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
#include "thread.h"
using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void uci_loop();
extern void benchmark(int argc, char* argv[]);
extern void init_kpk_bitbase();
extern void kpk_bitbase_init();
int main(int argc, char* argv[]) {
// Disable IO buffering for C and C++ standard libraries
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations
init_bitboards();
Position::init_zobrist();
Position::init_piece_square_tables();
init_kpk_bitbase();
init_search();
bitboards_init();
Position::init();
kpk_bitbase_init();
Search::init();
Threads.init();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
#endif
cout << engine_info() << endl;
if (argc < 2)
{
// Print copyright notice
cout << engine_name() << " by " << engine_authors() << endl;
if (argc == 1)
uci_loop();
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT." << endl;
// Wait for a command from the user, and passes this command to
// execute_uci_command() and also intercepts EOF from stdin to
// ensure that we exit gracefully if the GUI dies unexpectedly.
string cmd;
while (getline(cin, cmd) && execute_uci_command(cmd)) {}
}
else if (string(argv[1]) == "bench" && argc < 8)
else if (string(argv[1]) == "bench")
benchmark(argc, argv);
else
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
cerr << "\nUsage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
Threads.exit();
return 0;
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include <cassert>
#include <cstring>
#include <algorithm>
#include "material.h"
@@ -49,13 +50,13 @@ namespace {
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
Endgame<Value, KmmKm> EvaluateKmmKm[] = { Endgame<Value, KmmKm>(WHITE), Endgame<Value, KmmKm>(BLACK) };
Endgame<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) };
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, KPKP>(BLACK) };
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
@@ -89,15 +90,15 @@ void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// MaterialInfoTable::material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
Key key = pos.get_material_key();
Key key = pos.material_key();
MaterialInfo* mi = probe(key);
// If mi->key matches the position's material hash key, it means that we
@@ -117,7 +118,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
// Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed.
if ((mi->evaluationFunction = funcs->get<EndgameBase<Value> >(key)) != NULL)
if ((mi->evaluationFunction = funcs->get<Value>(key)) != NULL)
return mi;
if (is_KXK<WHITE>(pos))
@@ -142,7 +143,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm[WHITE];
mi->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return mi;
}
}
@@ -154,7 +155,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
// scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL)
if ((sf = funcs->get<ScaleFactor>(key)) != NULL)
{
mi->scalingFunction[sf->color()] = sf;
return mi;
@@ -203,13 +204,13 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{
mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(WHITE, BISHOP), 2)]);
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{
mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(BLACK, BISHOP), 2)]);
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight
@@ -253,7 +254,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
@@ -261,7 +262,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
v = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,13 @@
const int MaterialTableSize = 8192;
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in
@@ -61,13 +68,13 @@ private:
/// The MaterialInfoTable class represents a pawn hash table. The most important
/// method is get_material_info, which returns a pointer to a MaterialInfo object.
/// method is material_info(), which returns a pointer to a MaterialInfo object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public:
~MaterialInfoTable();
void init();
MaterialInfo* get_material_info(const Position& pos) const;
MaterialInfo* material_info(const Position& pos) const;
static Phase game_phase(const Position& pos);
private:
@@ -90,12 +97,12 @@ inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) cons
if (!scalingFunction[c])
return ScaleFactor(factor[c]);
ScaleFactor sf = scalingFunction[c]->apply(pos);
ScaleFactor sf = (*scalingFunction[c])(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
}
inline Value MaterialInfo::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos);
return (*evaluationFunction)(pos);
}
inline Score MaterialInfo::material_value() const {

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(_MSC_VER)
#if defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE
#define NOMINMAX // disable macros min() and max()
#include <windows.h>
#include <sys/timeb.h>
#else
# include <sys/time.h>
# include <sys/types.h>
@@ -26,18 +33,13 @@
# include <sys/pstat.h>
# endif
#else
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iomanip>
@@ -50,91 +52,69 @@
using namespace std;
/// Version number. If EngineVersion is left empty, then AppTag plus
/// current date (in the format YYMMDD) is used as a version number.
/// Version number. If Version is left empty, then Tag plus current
/// date (in the format YYMMDD) is used as a version number.
static const string AppName = "Stockfish";
static const string EngineVersion = "2.1";
static const string AppTag = "";
static const string Version = "2.2.1";
static const string Tag = "";
/// engine_name() returns the full name of the current Stockfish version.
/// engine_info() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending
/// on whether the constant EngineVersion is empty.
/// on whether Version is empty.
const string engine_name() {
const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(CpuIs64Bit ? " 64bit" : "");
const string cpu64(Is64Bit ? " 64bit" : "");
const string popcnt(HasPopCnt ? " SSE4.2" : "");
if (!EngineVersion.empty())
return AppName + " " + EngineVersion + cpu64;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
date >> month >> day >> year;
if (Version.empty())
{
date >> month >> day >> year;
s << setfill('0') << AppName + " " + AppTag + " "
<< year.substr(2, 2) << setw(2)
<< (1 + months.find(month) / 4) << setw(2)
<< day << cpu64;
s << "Stockfish " << Tag
<< setfill('0') << " " << year.substr(2)
<< setw(2) << (1 + months.find(month) / 4)
<< setw(2) << day << cpu64 << popcnt;
}
else
s << "Stockfish " << Version << cpu64 << popcnt;
s << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str();
}
/// Our brave developers! Required by UCI
/// Debug functions used mainly to collect run-time statistics
const string engine_authors() {
return "Tord Romstad, Marco Costalba and Joona Kiiski";
}
/// Debug stuff. Helper functions used mainly for debugging purposes
static uint64_t dbg_hit_cnt0;
static uint64_t dbg_hit_cnt1;
static uint64_t dbg_mean_cnt0;
static uint64_t dbg_mean_cnt1;
void dbg_print_hit_rate() {
if (dbg_hit_cnt0)
cout << "Total " << dbg_hit_cnt0 << " Hit " << dbg_hit_cnt1
<< " hit rate (%) " << 100 * dbg_hit_cnt1 / dbg_hit_cnt0 << endl;
}
void dbg_print_mean() {
if (dbg_mean_cnt0)
cout << "Total " << dbg_mean_cnt0 << " Mean "
<< (float)dbg_mean_cnt1 / dbg_mean_cnt0 << endl;
}
void dbg_mean_of(int v) {
dbg_mean_cnt0++;
dbg_mean_cnt1 += v;
}
void dbg_hit_on(bool b) {
dbg_hit_cnt0++;
if (b)
dbg_hit_cnt1++;
}
static uint64_t hits[2], means[2];
void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_before() { dbg_hit_on(false); }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
void dbg_mean_of(int v) { means[0]++; means[1] += v; }
void dbg_print() {
if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
if (means[0])
cerr << "Total " << means[0] << " Mean "
<< (float)means[1] / means[0] << endl;
}
/// get_system_time() returns the current system time, measured in milliseconds
/// system_time() returns the current system time, measured in milliseconds
int get_system_time() {
int system_time() {
#if defined(_MSC_VER)
struct _timeb t;
@@ -155,16 +135,16 @@ int cpu_count() {
#if defined(_MSC_VER)
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS);
return std::min(int(s.dwNumberOfProcessors), MAX_THREADS);
#else
# if defined(_SC_NPROCESSORS_ONLN)
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
return std::min((int)sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS);
return std::min((int)psd.psd_proc_cnt, MAX_THREADS);
# else
return 1;
# endif
@@ -173,78 +153,32 @@ int cpu_count() {
}
/// Check for console input. Original code from Beowulf, Olithink and Greko
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads.
#ifndef _WIN32
int input_available() {
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
timeout.tv_sec = 0; // Set to timeout immediately
timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout);
return (FD_ISSET(fileno(stdin), &readfds));
}
void timed_wait(WaitCondition* sleepCond, Lock* sleepLock, int msec) {
#if defined(_MSC_VER)
int tm = msec;
#else
struct timeval t;
struct timespec abstime, *tm = &abstime;
int input_available() {
gettimeofday(&t, NULL);
static HANDLE inh = NULL;
static bool usePipe = false;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
abstime.tv_sec = t.tv_sec + (msec / 1000);
abstime.tv_nsec = (t.tv_usec + (msec % 1000) * 1000) * 1000;
if (!inh)
if (abstime.tv_nsec > 1000000000LL)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode(inh, &nchars))
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
abstime.tv_sec += 1;
abstime.tv_nsec -= 1000000000LL;
}
// When using Standard C input functions, also check if there
// is anything in the buffer. After a call to such functions,
// the input waiting in the pipe will be copied to the buffer,
// and the call to PeekNamedPipe can indicate no input available.
// Setting stdin to unbuffered was not enough. [from Greko]
if (stdin->_cnt > 0)
return 1;
// When running under a GUI the input commands are sent to us
// directly over the internal pipe. If PeekNamedPipe() returns 0
// then something went wrong. Probably the parent program exited.
// Returning 1 will make the next call to the input function
// return EOF, where this should be catched then.
if (usePipe)
return PeekNamedPipe(inh, NULL, 0, NULL, &nchars, NULL) ? nchars : 1;
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &nchars);
// Read data from console without removing it from the buffer
if (nchars <= 0 || !PeekConsoleInput(inh, rec, Min(nchars, 256), &recCnt))
return 0;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
#endif
cond_timedwait(sleepCond, sleepLock, tm);
}
/// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be
@@ -257,11 +191,11 @@ void prefetch(char*) {}
void prefetch(char* addr) {
#if defined(__INTEL_COMPILER) || defined(__ICL)
# if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
# endif
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,22 +20,31 @@
#if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED
#include <fstream>
#include <string>
#include "lock.h"
#include "types.h"
extern const std::string engine_name();
extern const std::string engine_authors();
extern int get_system_time();
extern const std::string engine_info(bool to_uci = false);
extern int system_time();
extern int cpu_count();
extern int input_available();
extern void timed_wait(WaitCondition*, Lock*, int);
extern void prefetch(char* addr);
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern void dbg_print();
class Position;
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_uci(Move m, bool chess960);
extern const std::string move_to_san(Position& pos, Move m);
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
#endif // !defined(MISC_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,31 +19,22 @@
#include <cassert>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "move.h"
#include "movegen.h"
#include "search.h"
#include "position.h"
using std::string;
namespace {
const string time_string(int milliseconds);
const string score_string(Value v);
}
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode.
/// Chess960 mode. Instead internally Move is coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) {
Square from = move_from(m);
Square to = move_to(m);
Square from = from_sq(m);
Square to = to_sq(m);
string promotion;
if (m == MOVE_NONE)
@@ -52,14 +43,11 @@ const string move_to_uci(Move m, bool chess960) {
if (m == MOVE_NULL)
return "0000";
if (move_is_short_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8";
if (is_castle(m) && !chess960)
to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
if (move_is_long_castle(m) && !chess960)
return from == SQ_E1 ? "e1c1" : "e8c8";
if (move_is_promotion(m))
promotion = char(tolower(piece_type_to_char(move_promotion_piece(m))));
if (is_promotion(m))
promotion = char(tolower(piece_type_to_char(promotion_piece_type(m))));
return square_to_string(from) + square_to_string(to) + promotion;
}
@@ -71,88 +59,91 @@ const string move_to_uci(Move m, bool chess960) {
Move move_from_uci(const Position& pos, const string& str) {
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if (str == move_to_uci(cur->move, pos.is_chess960()))
return cur->move;
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move from the position. The return value is
/// that the move is a legal move for the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
MoveStack mlist[MAX_MOVES];
Square from = move_from(m);
Square to = move_to(m);
PieceType pt = pos.type_of_piece_on(from);
string san;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
assert(is_ok(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
Square sq, from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(pos.piece_on(from));
string san;
if (is_castle(m))
san = (to_sq(m) < from_sq(m) ? "O-O-O" : "O-O");
else
{
if (pt != PAWN)
{
san = piece_type_to_char(pt);
// Collect all legal moves of piece type 'pt' with destination 'to'
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
int f = 0, r = 0;
for (MoveStack* cur = mlist; cur != last; cur++)
if ( move_to(cur->move) == to
&& pos.type_of_piece_on(move_from(cur->move)) == pt)
{
if (square_file(move_from(cur->move)) == square_file(from))
f++;
if (square_rank(move_from(cur->move)) == square_rank(from))
r++;
}
assert(f > 0 && r > 0);
// Disambiguation if we have more then one piece with destination 'to'
if (f == 1 && r > 1)
san += file_to_char(square_file(from));
else if (f > 1 && r == 1)
san += rank_to_char(square_rank(from));
else if (f > 1 && r > 1)
san += square_to_string(from);
// note that for pawns is not needed because starting file is explicit.
attackers = pos.attackers_to(to) & pos.pieces(pt, pos.side_to_move());
clear_bit(&attackers, from);
ambiguousMove = ambiguousFile = ambiguousRank = false;
while (attackers)
{
sq = pop_1st_bit(&attackers);
// Pinned pieces are not included in the possible sub-set
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
if (file_of(sq) == file_of(from))
ambiguousFile = true;
if (rank_of(sq) == rank_of(from))
ambiguousRank = true;
ambiguousMove = true;
}
if (ambiguousMove)
{
if (!ambiguousFile)
san += file_to_char(file_of(from));
else if (!ambiguousRank)
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
}
if (pos.move_is_capture(m))
if (pos.is_capture(m))
{
if (pt == PAWN)
san += file_to_char(square_file(from));
san += file_to_char(file_of(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
if (is_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion_piece(m));
san += piece_type_to_char(promotion_piece_type(m));
}
}
@@ -166,93 +157,3 @@ const string move_to_san(Position& pos, Move m) {
return san;
}
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
const int startColumn = 28;
const size_t maxLength = 80 - startColumn;
const string lf = string("\n") + string(startColumn, ' ');
StateInfo state[PLY_MAX_PLUS_2], *st = state;
Move* m = pv;
string san;
std::stringstream s;
size_t length = 0;
// First print depth, score, time and searched nodes...
s << std::setw(2) << depth
<< std::setw(8) << score_string(score)
<< std::setw(8) << time_string(time);
if (pos.nodes_searched() < M)
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << std::setw(7) << pos.nodes_searched() / K << "K ";
else
s << std::setw(7) << pos.nodes_searched() / M << "M ";
// ...then print the full PV line in short algebraic notation
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
length += san.length() + 1;
if (length > maxLength)
{
length = san.length() + 1;
s << lf;
}
s << san << ' ';
pos.do_move(*m++, *st++);
}
// Restore original position before to leave
while (m != pv) pos.undo_move(*--m);
return s.str();
}
namespace {
const string time_string(int millisecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int hours = millisecs / MSecHour;
int minutes = (millisecs % MSecHour) / MSecMinute;
int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
std::stringstream s;
if (hours)
s << hours << ':';
s << std::setfill('0') << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const string score_string(Value v) {
std::stringstream s;
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << std::setprecision(2) << std::fixed << std::showpos << float(v) / PawnValueMidgame;
return s.str();
}
}

View File

@@ -1,195 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
#include <string>
#include "misc.h"
#include "types.h"
// Maximum number of allowed moves per position
const int MAX_MOVES = 256;
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in
/// because in any normal move destination square is always different
/// from origin square while MOVE_NONE and MOVE_NULL have the same
/// origin and destination square, 0 and 1 respectively.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.score; }
// An helper insertion sort implementation, works with pointers and iterators
template<typename T, typename K>
inline void insertion_sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
// Our dedicated sort in range [firstMove, lastMove), first splits
// positive scores from ramining then order seaprately the two sets.
template<typename T>
inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
{
T tmp;
T *p, *d;
d = lastMove;
p = firstMove - 1;
d->score = -1; // right guard
// Split positives vs non-positives
do {
while ((++p)->score > 0) {}
if (p != d)
{
while (--d != p && d->score <= 0) {}
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort just positive scored moves, remaining only when we get there
insertion_sort<T, T*>(firstMove, p);
*lastPositive = p;
}
// Picks up the best move in range [curMove, lastMove), one per cycle.
// It is faster then sorting all the moves in advance when moves are few,
// as normally are the possible captures. Note that is not a stable alghoritm.
template<typename T>
inline T pick_best(T* curMove, T* lastMove)
{
T bestMove, tmp;
bestMove = *curMove;
while (++curMove != lastMove)
{
if (bestMove < *curMove)
{
tmp = *curMove;
*curMove = bestMove;
bestMove = tmp;
}
}
return bestMove;
}
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 0x3F);
}
inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline bool move_is_special(Move m) {
return m & (3 << 14);
}
inline bool move_is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int move_is_ep(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int move_is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline bool move_is_short_castle(Move m) {
return move_is_castle(m) && (move_to(m) > move_from(m));
}
inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline PieceType move_promotion_piece(Move m) {
return move_is_promotion(m) ? PieceType(((int(m) >> 12) & 3) + 2) : PIECE_TYPE_NONE;
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | ((int(promotion) - 2) << 12) | (1 << 14));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (3 << 14));
}
inline bool move_is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NONE
}
class Position;
extern const std::string move_to_uci(Move m, bool chess960);
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_san(Position& pos, Move m);
extern const std::string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
#endif // !defined(MOVE_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,9 +18,11 @@
*/
#include <cassert>
#include <algorithm>
#include "bitcount.h"
#include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'.
@@ -45,14 +47,13 @@ namespace {
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN);
assert(Pt != QUEEN && Pt != PAWN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
SERIALIZE_MOVES(b);
return mlist;
}
@@ -60,13 +61,13 @@ namespace {
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING);
assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
const Square* pl = pos.piece_list(us, Pt);
if ((from = *ptr++) == SQ_NONE)
if ((from = *pl++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
@@ -84,7 +85,7 @@ namespace {
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
} while ((from = *pl++) != SQ_NONE);
return mlist;
}
@@ -100,8 +101,6 @@ namespace {
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN);
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
}
@@ -111,15 +110,15 @@ namespace {
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
const Square* pl = pos.piece_list(us, Pt);
if (*ptr != SQ_NONE)
if (*pl != SQ_NONE)
{
do {
from = *ptr;
from = *pl;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
} while (*++pl != SQ_NONE);
}
return mlist;
}
@@ -150,40 +149,34 @@ namespace {
template<MoveType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_NON_EVASION);
assert(!pos.in_check());
Color us = pos.side_to_move();
Bitboard target;
if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
target = pos.pieces_of_color(opposite_color(us));
if (Type == MV_CAPTURE)
target = pos.pieces(flip(us));
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else
assert(false);
if (Type == MV_NON_EVASION)
{
mlist = generate_piece_moves<PAWN, MV_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, MV_NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
}
else
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
else if (Type == MV_NON_EVASION)
target = pos.pieces(flip(us)) | pos.empty_squares();
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
if (Type != MV_CAPTURE)
if (Type != MV_CAPTURE && pos.can_castle(us))
{
if (pos.can_castle_kingside(us))
if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle_queenside(us))
if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
}
@@ -196,28 +189,27 @@ template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mli
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
/// generate<MV_NON_CAPTURE_CHECK> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us));
Square ksq = pos.king_square(flip(us));
assert(pos.piece_on(ksq) == make_piece(opposite_color(us), KING));
assert(pos.piece_on(ksq) == make_piece(flip(us), KING));
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us);
b = dc = pos.discovered_check_candidates();
while (b)
{
from = pop_1st_bit(&b);
switch (pos.type_of_piece_on(from))
switch (type_of(pos.piece_on(from)))
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
@@ -237,12 +229,11 @@ MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist)
}
/// 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.
/// generate<MV_EVASION> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.in_check());
Bitboard b, target;
@@ -251,7 +242,7 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB;
Bitboard sliderAttacks = 0;
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
@@ -265,26 +256,31 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
checkersCnt++;
checksq = pop_1st_bit(&b);
assert(pos.color_of_piece_on(checksq) == opposite_color(us));
assert(color_of(pos.piece_on(checksq)) == flip(us));
switch (pos.type_of_piece_on(checksq))
switch (type_of(pos.piece_on(checksq)))
{
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN:
// In case of a queen remove also squares attacked in the other direction to
// avoid possible illegal moves when queen and king are on adjacent squares.
if (RookPseudoAttacks[checksq] & (1ULL << ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
// If queen and king are far we can safely remove all the squares attacked
// in the other direction becuase are not reachable by the king anyway.
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq)))
sliderAttacks |= QueenPseudoAttacks[checksq];
// Otherwise, if king and queen are adjacent and on a diagonal line, we need to
// use real rook attacks to check if king is safe to move in the other direction.
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1.
else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
}
} while (b);
// Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq;
SERIALIZE_MOVES(b);
@@ -304,26 +300,16 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
}
/// generate<MV_LEGAL / MV_PSEUDO_LEGAL> computes a complete list of legal
/// or pseudo-legal moves in the current position.
template<>
MoveStack* generate<MV_PSEUDO_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
return pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
}
/// generate<MV_LEGAL> computes a complete list of legal moves in the current position
template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
Bitboard pinned = pos.pinned_pieces();
last = generate<MV_PSEUDO_LEGAL>(pos, mlist);
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
// Remove illegal moves from the list
while (cur != last)
@@ -346,7 +332,7 @@ namespace {
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<MoveType Type, Square Delta>
template<Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
@@ -360,7 +346,7 @@ namespace {
return mlist;
}
template<Color Us, MoveType Type, Square Delta>
template<MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
@@ -378,21 +364,21 @@ namespace {
{
to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION)
if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
(*mlist++).move = make_promotion(to - Delta, to, ROOK);
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us))))
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Delta > 0 ? BLACK : WHITE)))
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
}
return mlist;
@@ -406,21 +392,21 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const Square TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const Square TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces_of_color(Them));
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them));
// Pre-calculate pawn pushes before changing emptySquares definition
if (Type != MV_CAPTURE)
{
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
pawnPushes = move_pawns<UP>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == MV_EVASION)
@@ -436,23 +422,23 @@ namespace {
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB;
mlist = generate_promotions<Us, Type, TDELTA_NE>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_NW>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_N >(pos, mlist, pawnsOn7, emptySquares);
mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
}
// Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION)
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<RIGHT_UP>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<LEFT_UP>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
@@ -465,37 +451,38 @@ namespace {
// don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard
{
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares;
dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
SERIALIZE_MOVES_D(b1, -UP);
SERIALIZE_MOVES_D(b2, -UP -UP);
}
// En passant captures
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
if ( (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
&& pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6);
assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3);
// 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 a discovery check and we are forced to do otherwise.
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB);
assert(b1);
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_ep_move(to, pos.ep_square());
(*mlist++).move = make_enpassant(to, pos.ep_square());
}
}
return mlist;
@@ -504,37 +491,45 @@ namespace {
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us);
Color them = flip(us);
assert(pos.piece_on(ksq) == make_piece(us, KING));
// After castling, the rook and king's final positions are exactly the same
// in Chess960 as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(f);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
assert(pos.piece_on(rsq) == make_piece(us, ROOK));
// Unimpeded rule: All the squares between the king's initial and final squares
// (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & pos.pieces(them)))
return mlist;
// It is a bit complicated to correctly handle Chess960
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (pos.is_chess960())
{
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, rfrom);
if (pos.attackers_to(kto, occ) & pos.pieces(them))
return mlist;
}
if ( Side == QUEEN_SIDE
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, QUEEN)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
(*mlist++).move = make_castle(kfrom, rfrom);
return mlist;
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,7 @@
#if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED
#include "move.h"
#include "position.h"
#include "types.h"
enum MoveType {
MV_CAPTURE,
@@ -30,11 +29,28 @@ enum MoveType {
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL,
MV_PSEUDO_LEGAL
MV_LEGAL
};
class Position;
template<MoveType>
MoveStack* generate(const Position& pos, MoveStack* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
/// handy to use this class instead of the low level generate() function.
template<MoveType T>
struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
void operator++() { cur++; }
bool end() const { return cur == last; }
Move move() const { return cur->move; }
int size() const { return int(last - mlist); }
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "movegen.h"
@@ -28,93 +29,127 @@
namespace {
enum MovegenPhase {
PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_TT_MOVE, // Transposition table move
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= captureThreshold (captureThreshold <= 0)
PH_GOOD_PROBCUT, // Queen promotions and captures with SEE values > captureThreshold (captureThreshold >= 0)
PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES, // Non-captures and underpromotions
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_NONCAPTURES_1, // Non-captures and underpromotions with positive score
PH_NONCAPTURES_2, // Non-captures and underpromotions with non-positive score
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < captureThreshold (captureThreshold <= 0)
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QRECAPTURES, // Recaptures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
};
CACHE_LINE_ALIGNMENT
const uint8_t MainSearchTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP };
const uint8_t MainSearchTable[] = { PH_TT_MOVE, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES_1, PH_NONCAPTURES_2, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVE, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_STOP };
const uint8_t QsearchRecapturesTable[] = { PH_TT_MOVE, PH_QRECAPTURES, PH_STOP };
const uint8_t ProbCutTable[] = { PH_TT_MOVE, PH_GOOD_PROBCUT, PH_STOP };
// Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& move) { return move.score > 0; }
// Picks and pushes to the front the best move in range [firstMove, lastMove),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
{
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
return firstMove;
}
}
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// Constructors for the MovePicker class. As arguments we pass information
/// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
SearchStack* ss, Value beta) : pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) {
captureThreshold = 0;
badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
{
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
else
{
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low
// depth and far from beta.
// Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
badCaptureThreshold = -PawnValueMidgame;
captureThreshold = -PawnValueMidgame;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->eval > beta)
captureThreshold = beta - ss->eval;
phasePtr = MainSearchTable;
}
phasePtr += int(!searchTT) - 1;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h)
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, Square recaptureSq)
: pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable;
else
else if (d >= DEPTH_QS_RECAPTURES)
{
phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.move_is_capture_or_promotion(ttm))
searchTT = ttMoves[0].move = MOVE_NONE;
if (ttm != MOVE_NONE && !pos.is_capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else
{
phasePtr = QsearchRecapturesTable;
recaptureSquare = recaptureSq;
ttm = MOVE_NONE;
}
phasePtr += int(!searchTT) - 1;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType parentCapture)
: pos(p), H(h) {
assert (!pos.in_check());
// In ProbCut we consider only captures better than parent's move
captureThreshold = PieceValueMidgame[Piece(parentCapture)];
phasePtr = ProbCutTable;
if ( ttm != MOVE_NONE
&& (!pos.is_capture(ttm) || pos.see(ttm) <= captureThreshold))
ttm = MOVE_NONE;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
@@ -128,12 +163,12 @@ void MovePicker::go_next_phase() {
phase = *(++phasePtr);
switch (phase) {
case PH_TT_MOVES:
curMove = ttMoves;
lastMove = curMove + 2;
case PH_TT_MOVE:
lastMove = curMove + 1;
return;
case PH_GOOD_CAPTURES:
case PH_GOOD_PROBCUT:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
@@ -143,10 +178,18 @@ void MovePicker::go_next_phase() {
lastMove = curMove + 2;
return;
case PH_NONCAPTURES:
lastMove = generate<MV_NON_CAPTURE>(pos, moves);
case PH_NONCAPTURES_1:
lastNonCapture = lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture);
lastMove = std::partition(curMove, lastMove, has_positive_score);
sort<MoveStack>(curMove, lastMove);
return;
case PH_NONCAPTURES_2:
curMove = lastMove;
lastMove = lastNonCapture;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(curMove, lastMove);
return;
case PH_BAD_CAPTURES:
@@ -167,6 +210,10 @@ void MovePicker::go_next_phase() {
score_captures();
return;
case PH_QRECAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
return;
case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
@@ -185,7 +232,7 @@ void MovePicker::go_next_phase() {
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be
/// picked first by get_next_move().
/// picked first by next_move().
void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA.
@@ -207,11 +254,11 @@ void MovePicker::score_captures() {
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
if (move_is_promotion(m))
cur->score = QueenValueMidgame;
else
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m));
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
- type_of(pos.piece_on(from_sq(m)));
if (is_promotion(m))
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
}
}
@@ -223,8 +270,8 @@ void MovePicker::score_noncaptures() {
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
from = move_from(m);
cur->score = H.value(pos.piece_on(from), move_to(m));
from = from_sq(m);
cur->score = H.value(pos.piece_on(from), to_sq(m));
}
}
@@ -245,22 +292,22 @@ void MovePicker::score_evasions() {
m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + History::MaxValue;
else if (pos.is_capture(m))
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
- type_of(pos.piece_on(from_sq(m))) + History::MaxValue;
else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
cur->score = H.value(pos.piece_on(from_sq(m)), to_sq(m));
}
}
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// MovePicker::next_move() is the most important method of the MovePicker class.
/// It returns a new pseudo legal move every time it is called, until there
/// are no more moves left. It picks the move with the biggest score from a list
/// of generated moves taking care not to return the tt move if has already been
/// searched previously. Note that this function is not thread safe so should be
/// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::get_next_move() {
Move MovePicker::next_move() {
Move move;
@@ -271,71 +318,73 @@ Move MovePicker::get_next_move() {
switch (phase) {
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned))
return move;
case PH_TT_MOVE:
curMove++;
return ttMove;
break;
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
move = pick_best(curMove++, lastMove)->move;
if (move != ttMove)
{
assert(captureThreshold <= 0); // Otherwise we must use see instead of see_sign
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold)
if (seeValue >= captureThreshold)
return move;
// Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality.
// Losing capture, move it to the tail of the array
(--badCaptures)->move = move;
badCaptures->score = seeValue;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
case PH_GOOD_PROBCUT:
move = pick_best(curMove++, lastMove)->move;
if ( move != ttMove
&& pos.see(move) > captureThreshold)
return move;
break;
case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
case PH_KILLERS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move)
&& move != ttMove
&& !pos.is_capture(move))
return move;
break;
case PH_NONCAPTURES_1:
case PH_NONCAPTURES_2:
move = (curMove++)->move;
if ( move != ttMove
&& move != killers[0].move
&& move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
&& move != killers[1].move)
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
move = pick_best(curMove++, lastMove)->move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
move = pick_best(curMove++, lastMove)->move;
if (move != ttMove)
return move;
break;
case PH_QRECAPTURES:
move = (curMove++)->move;
if (to_sq(move) == recaptureSquare)
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
if (move != ttMove)
return move;
break;

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,17 +21,15 @@
#define MOVEPICK_H_INCLUDED
#include "history.h"
#include "move.h"
#include "position.h"
#include "search.h"
#include "types.h"
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// MovePicker is a class which is used to pick one pseudo legal move at a time
/// from the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::get_next_move(), which returns a new legal move each time it
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
/// MovePicker::next_move(), which returns a new pseudo legal move each time
/// it is called, until there are no moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to get a cut-off first.
@@ -40,9 +38,10 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public:
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value);
MovePicker(const Position&, Move, Depth, const History&);
Move get_next_move();
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&, Square recaptureSq);
MovePicker(const Position&, Move, const History&, PieceType parentCapture);
Move next_move();
private:
void score_captures();
@@ -52,11 +51,13 @@ private:
const Position& pos;
const History& H;
Bitboard pinned;
MoveStack ttMoves[2], killers[2];
int badCaptureThreshold, phase;
Depth depth;
Move ttMove;
MoveStack killers[2];
Square recaptureSquare;
int captureThreshold, phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack *curMove, *lastMove, *lastNonCapture, *badCaptures;
MoveStack moves[MAX_MOVES];
};

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -72,16 +72,14 @@ namespace {
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// PawnInfoTable::pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
Key key = pos.pawn_key();
PawnInfo* pi = probe(key);
// If pi->key matches the position's pawn hash key, it means that we
@@ -118,7 +116,6 @@ template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
@@ -127,15 +124,15 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const Square* ptr = pos.piece_list_begin(Us, PAWN);
const Square* pl = pos.piece_list(Us, PAWN);
// Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
while ((s = *pl++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s);
r = square_rank(s);
f = file_of(s);
r = rank_of(s);
// This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f);
@@ -184,8 +181,8 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// pawn on neighboring files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
@@ -225,12 +222,12 @@ Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq);
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(file_of(ksq));
r = ksq & (7 << 3);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i);
shelter += BitCount8Bit[(pawns >> r) & 0xFF] << (6 - i);
}
}
kingSquares[Us] = ksq;

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,9 +29,9 @@ const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object.
/// to add further information in the future. A lookup to the pawn hash
/// table (performed by calling the pawn_info method in a PawnInfoTable
/// object) returns a pointer to a PawnInfo object.
class PawnInfo {
@@ -63,11 +63,11 @@ private:
/// The PawnInfoTable class represents a pawn hash table. The most important
/// method is get_pawn_info, which returns a pointer to a PawnInfo object.
/// method is pawn_info, which returns a pointer to a PawnInfo object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public:
PawnInfo* get_pawn_info(const Position& pos) const;
PawnInfo* pawn_info(const Position& pos) const;
private:
template<Color Us>

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,44 +20,23 @@
#if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED
#include <cassert>
#include "bitboard.h"
#include "move.h"
#include "types.h"
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move).
const int MaxGameLength = 220;
/// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check.
class Position;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo {
explicit CheckInfo(const Position&);
explicit CheckInfo(const Position&);
Bitboard dcCandidates;
Bitboard checkSq[8];
Square ksq;
};
/// Castle rights, encoded as bit fields
enum CastleRights {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[8];
};
@@ -68,14 +47,14 @@ enum Phase {
struct StateInfo {
Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare;
Score value;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score value;
Square epSquare;
PieceType capturedType;
Key key;
Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous;
};
@@ -104,35 +83,24 @@ struct StateInfo {
class Position {
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
// No copy c'tor or assignment operator allowed
Position(const Position&);
Position& operator=(const Position&);
public:
enum GamePhase {
MidGame,
EndGame
};
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
Position() {}
Position(const Position& pos, int th) { copy(pos, th); }
Position(const std::string& fen, bool isChess960, int th);
// Text input/output
void copy(const Position& pos, int th);
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
// Copying
void flip();
// The piece on a given square
Piece piece_on(Square s) const;
PieceType type_of_piece_on(Square s) const;
Color color_of_piece_on(Square s) const;
bool square_is_empty(Square s) const;
bool square_is_occupied(Square s) const;
Value midgame_value_of_piece_on(Square s) const;
Value endgame_value_of_piece_on(Square s) const;
// Side to move
Color side_to_move() const;
@@ -140,7 +108,7 @@ public:
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const;
Bitboard pieces(Color c) const;
Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
@@ -156,42 +124,37 @@ public:
Square king_square(Color c) const;
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(CastleRight f) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
Square castle_rook_square(CastleRight f) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const;
// Checking pieces and under check information
Bitboard checkers() const;
bool in_check() const;
// Piece lists
Square piece_list(Color c, PieceType pt, int index) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
const Square* piece_list(Color c, PieceType pt) const;
// Information about attacks to or from a given square
Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool move_is_legal(const Move m) const;
bool move_is_legal(const Move m, Bitboard pinned) const;
bool move_gives_check(Move m) const;
bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool is_pseudo_legal(const Move m) const;
bool is_capture(Move m) const;
bool is_capture_or_promotion(Move m) const;
bool is_passed_pawn_push(Move m) const;
// Piece captured with previous moves
PieceType captured_piece_type() const;
@@ -199,38 +162,32 @@ public:
// Information about pawns
bool pawn_is_passed(Color c, Square s) const;
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
void do_null_move(StateInfo& st);
void undo_null_move();
template<bool Do> void do_null_move(StateInfo& st);
// Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const;
int see_sign(Move m) const;
// Accessing hash keys
Key get_key() const;
Key get_exclusion_key() const;
Key get_pawn_key() const;
Key get_material_key() const;
Key key() const;
Key exclusion_key() const;
Key pawn_key() const;
Key material_key() const;
// Incremental evaluation
Score value() const;
Value non_pawn_material(Color c) const;
static Score pst_delta(Piece piece, Square from, Square to);
Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks
bool is_mate() const;
bool is_draw() const;
template<bool SkipRepetition> bool is_draw() const;
// Number of plies from starting position
// Plies from start position to the beginning of search
int startpos_ply_counter() const;
// Other properties of the position
@@ -245,30 +202,23 @@ public:
void set_nodes_searched(int64_t n);
// Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const;
bool pos_is_ok(int* failedStep = NULL) const;
void flip_me();
// Static member functions
static void init_zobrist();
static void init_piece_square_tables();
// Global initialization
static void init();
private:
// Initialization helper functions (used while setting up a position)
void clear();
void detach();
void put_piece(Piece p, Square s);
void do_allow_oo(Color c);
void do_allow_ooo(Color c);
bool set_castling_rights(char token);
void set_castle_right(Square ksq, Square rsq);
bool move_is_legal(const Move m) const;
// Helper functions for doing and undoing moves
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep);
void do_castle_move(Move m);
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Helper template functions
template<bool Do> void do_castle_move(Move m);
template<bool FindPinned> Bitboard hidden_checkers() const;
// Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const;
@@ -276,45 +226,43 @@ private:
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
static Score pst(Color c, PieceType pt, Square s);
Score pst(Piece p, Square s) const;
Score compute_value() const;
Value compute_non_pawn_material(Color c) const;
// Board
Piece board[64];
Piece board[64]; // [square]
// Bitboards
Bitboard byTypeBB[8], byColorBB[2];
Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color]
Bitboard occupied;
// Piece counts
int pieceCount[2][8]; // [color][pieceType]
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Other info
Color sideToMove;
Key history[MaxGameLength];
int castleRightsMask[64];
int castleRightsMask[64]; // [square]
Square castleRookSquare[16]; // [castleRight]
StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes;
int startPosPly;
Color sideToMove;
int threadID;
StateInfo* st;
int chess960;
// Static variables
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
static Score pieceSquareTable[16][64]; // [piece][square]
static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count]
static Key zobEp[64]; // [square]
static Key zobCastle[16]; // [castleRight]
static Key zobSideToMove;
static Score PieceSquareTable[16][64];
static Key zobExclusion;
static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
};
inline int64_t Position::nodes_searched() const {
@@ -329,28 +277,8 @@ inline Piece Position::piece_on(Square s) const {
return board[s];
}
inline Color Position::color_of_piece_on(Square s) const {
return color_of_piece(piece_on(s));
}
inline PieceType Position::type_of_piece_on(Square s) const {
return type_of_piece(piece_on(s));
}
inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == PIECE_NONE;
}
inline bool Position::square_is_occupied(Square s) const {
return !square_is_empty(s);
}
inline Value Position::midgame_value_of_piece_on(Square s) const {
return PieceValueMidgame[piece_on(s)];
}
inline Value Position::endgame_value_of_piece_on(Square s) const {
return PieceValueEndgame[piece_on(s)];
return board[s] == NO_PIECE;
}
inline Color Position::side_to_move() const {
@@ -358,14 +286,14 @@ inline Color Position::side_to_move() const {
}
inline Bitboard Position::occupied_squares() const {
return byTypeBB[0];
return occupied;
}
inline Bitboard Position::empty_squares() const {
return ~occupied_squares();
return ~occupied;
}
inline Bitboard Position::pieces_of_color(Color c) const {
inline Bitboard Position::pieces(Color c) const {
return byColorBB[c];
}
@@ -389,11 +317,7 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt];
}
inline Square Position::piece_list(Color c, PieceType pt, int idx) const {
return pieceList[c][pt][idx];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
inline const Square* Position::piece_list(Color c, PieceType pt) const {
return pieceList[c][pt];
}
@@ -405,24 +329,16 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0];
}
inline bool Position::can_castle_kingside(Color side) const {
return st->castleRights & (1+int(side));
inline bool Position::can_castle(CastleRight f) const {
return st->castleRights & f;
}
inline bool Position::can_castle_queenside(Color side) const {
return st->castleRights & (4+4*int(side));
inline bool Position::can_castle(Color c) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << c);
}
inline bool Position::can_castle(Color side) const {
return can_castle_kingside(side) || can_castle_queenside(side);
}
inline Square Position::initial_kr_square(Color c) const {
return relative_square(c, make_square(initialKRFile, RANK_1));
}
inline Square Position::initial_qr_square(Color c) const {
return relative_square(c, make_square(initialQRFile, RANK_1));
inline Square Position::castle_rook_square(CastleRight f) const {
return castleRookSquare[f];
}
template<>
@@ -450,44 +366,56 @@ inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
}
inline Bitboard Position::attacks_from(Piece p, Square s) const {
return attacks_from(p, s, occupied_squares());
}
inline Bitboard Position::attackers_to(Square s) const {
return attackers_to(s, occupied_squares());
}
inline Bitboard Position::checkers() const {
return st->checkersBB;
}
inline bool Position::in_check() const {
return st->checkersBB != EmptyBoardBB;
return st->checkersBB != 0;
}
inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>();
}
inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>();
}
inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s));
}
inline bool Position::square_is_weak(Square s, Color c) const {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
}
inline Key Position::get_key() const {
inline Key Position::key() const {
return st->key;
}
inline Key Position::get_exclusion_key() const {
inline Key Position::exclusion_key() const {
return st->key ^ zobExclusion;
}
inline Key Position::get_pawn_key() const {
inline Key Position::pawn_key() const {
return st->pawnKey;
}
inline Key Position::get_material_key() const {
inline Key Position::material_key() const {
return st->materialKey;
}
inline Score Position::pst(Color c, PieceType pt, Square s) {
return PieceSquareTable[make_piece(c, pt)][s];
inline Score Position::pst(Piece p, Square s) const {
return pieceSquareTable[p][s];
}
inline Score Position::pst_delta(Piece piece, Square from, Square to) {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return pieceSquareTable[piece][to] - pieceSquareTable[piece][from];
}
inline Score Position::value() const {
@@ -498,21 +426,21 @@ inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c];
}
inline bool Position::move_is_passed_pawn_push(Move m) const {
inline bool Position::is_passed_pawn_push(Move m) const {
Color c = side_to_move();
return piece_on(move_from(m)) == make_piece(c, PAWN)
&& pawn_is_passed(c, move_to(m));
return board[from_sq(m)] == make_piece(sideToMove, PAWN)
&& pawn_is_passed(sideToMove, to_sq(m));
}
inline int Position::startpos_ply_counter() const {
return startPosPlyCounter;
return startPosPly + st->pliesFromNull; // HACK
}
inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
}
inline bool Position::has_pawn_on_7th(Color c) const {
@@ -523,16 +451,17 @@ inline bool Position::is_chess960() const {
return chess960;
}
inline bool Position::move_is_capture(Move m) const {
inline bool Position::is_capture_or_promotion(Move m) const {
// Move must not be MOVE_NONE !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
assert(is_ok(m));
return is_special(m) ? !is_castle(m) : !square_is_empty(to_sq(m));
}
inline bool Position::move_is_capture_or_promotion(Move m) const {
inline bool Position::is_capture(Move m) const {
// Move must not be MOVE_NONE !
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
// Note that castle is coded as "king captures the rook"
assert(is_ok(m));
return (!square_is_empty(to_sq(m)) && !is_castle(m)) || is_enpassant(m);
}
inline PieceType Position::captured_piece_type() const {
@@ -543,12 +472,4 @@ inline int Position::thread() const {
return threadID;
}
inline void Position::do_allow_oo(Color c) {
st->castleRights |= (1 + int(c));
}
inline void Position::do_allow_ooo(Color c) {
st->castleRights |= (4 + 4*int(c));
}
#endif // !defined(POSITION_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,164 +22,77 @@
#include "types.h"
namespace {
#define S(mg, eg) make_score(mg, eg)
////
//// Constants modified by Joona Kiiski
////
const Value MP = PawnValueMidgame;
const Value MK = KnightValueMidgame;
const Value MB = BishopValueMidgame;
const Value MR = RookValueMidgame;
const Value MQ = QueenValueMidgame;
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
/// for white side, for black side the tables are symmetric.
const Value EP = PawnValueEndgame;
const Value EK = KnightValueEndgame;
const Value EB = BishopValueEndgame;
const Value ER = RookValueEndgame;
const Value EQ = QueenValueEndgame;
const int MgPST[][64] = {
static const Score PSQT[][64] = {
{ },
{// Pawn
// A B C D E F G H
0, 0, 0, 0, 0, 0, 0, 0,
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28,
MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28,
MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28,
MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28,
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
0, 0, 0, 0, 0, 0, 0, 0
{ // 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(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-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(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-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(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-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( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
},
{// Knight
// A B C D E F G H
MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135,
MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93,
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25,
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
{ // Knight
S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104),
S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79),
S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55),
S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42),
S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42),
S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55),
S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79),
S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104)
},
{// Bishop
// A B C D E F G H
MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40,
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
{ // Bishop
S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59),
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59)
},
{// Rook
// A B C D E F G H
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
{ // Rook
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3)
},
{// Queen
// A B C D E F G H
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
{ // Queen
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80),
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80)
},
{// King
//A B C D E F G H
287, 311, 262, 214, 214, 262, 311, 287,
262, 287, 238, 190, 190, 238, 287, 262,
214, 238, 190, 142, 142, 190, 238, 214,
190, 214, 167, 119, 119, 167, 214, 190,
167, 190, 142, 94, 94, 142, 190, 167,
142, 167, 119, 69, 69, 119, 167, 142,
119, 142, 94, 46, 46, 94, 142, 119,
94, 119, 69, 21, 21, 69, 119, 94
{ // King
S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18),
S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77),
S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105),
S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135),
S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135),
S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105),
S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77),
S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18)
}
};
const int EgPST[][64] = {
{ },
{// Pawn
// A B C D E F G H
0, 0, 0, 0, 0, 0, 0, 0,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
0, 0, 0, 0, 0, 0, 0, 0
},
{// Knight
// A B C D E F G H
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104
},
{// Bishop
// A B C D E F G H
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59
},
{// Rook
// A B C D E F G H
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3
},
{// Queen
// A B C D E F G H
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80
},
{// King
//A B C D E F G H
18, 77, 105, 135, 135, 105, 77, 18,
77, 135, 165, 193, 193, 165, 135, 77,
105, 165, 193, 222, 222, 193, 165, 105,
135, 193, 222, 251, 251, 222, 193, 135,
135, 193, 222, 251, 251, 222, 193, 135,
105, 165, 193, 222, 222, 193, 165, 105,
77, 135, 165, 193, 193, 165, 135, 77,
18, 77, 105, 135, 135, 105, 77, 18
}
};
} // namespace
#undef S
#endif // !defined(PSQTAB_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,19 +20,6 @@
available under the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
** George Marsaglia invented the RNG-Kiss-family in the early 90's.
** This is a specific version that Heinz van Saanen derived and
** tested from some public domain code by Bob Jenkins:
**
** Quite platform independent
** Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
** ~12 times faster than my *nix sys-rand()
** ~4 times faster than SSE2-version of Mersenne twister
** Average cycle length: ~2^126
** 64 bit seed
** Return doubles with a full 53 bit mantissa
** Thread safe
*/
#if !defined(RKISS_H_INCLUDED)
@@ -40,6 +27,20 @@
#include "types.h"
/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys.
/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a
/// specific version that Heinz van Saanen derived from some public domain code
/// by Bob Jenkins. Following the feature list, as tested by Heinz.
///
/// - Quite platform independent
/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
/// - ~12 times faster than my *nix sys-rand()
/// - ~4 times faster than SSE2-version of Mersenne twister
/// - Average cycle length: ~2^126
/// - 64 bit seed
/// - Return doubles with a full 53 bit mantissa
/// - Thread safe
class RKISS {
// Keep variables always together

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,53 +21,62 @@
#define SEARCH_H_INCLUDED
#include <cstring>
#include <vector>
#include "move.h"
#include "types.h"
class Position;
struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
namespace Search {
struct SearchStack {
/// The Stack struct keeps track of the information we need to remember from
/// nodes shallower and deeper in the tree during the search. Each search thread
/// has its own array of Stack objects, indexed by the current ply.
struct Stack {
SplitPoint* sp;
int ply;
Move currentMove;
Move mateKiller;
Move excludedMove;
Move bestMove;
Move killers[2];
Depth reduction;
Value eval;
Value evalMargin;
bool skipNullMove;
SplitPoint* sp;
int skipNullMove;
};
/// The SearchLimits struct stores information sent by GUI about available time
/// The LimitsType struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move.
struct SearchLimits {
struct LimitsType {
SearchLimits() { memset(this, 0, sizeof(SearchLimits)); }
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(maxTime | maxDepth | maxNodes | infinite); }
SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
: time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
maxNodes(mn), infinite(inf), ponder(pon) {}
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | int(infinite)); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes;
bool infinite, ponder;
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
};
extern void init_search();
/// The SignalsType struct stores volatile flags updated during the search
/// typically in an async fashion, for instance to stop the search by the GUI.
struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
};
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<Move> SearchMoves;
extern Position RootPosition;
extern void init();
extern int64_t perft(Position& pos, Depth depth);
extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
extern void think();
} // namespace
#endif // !defined(SEARCH_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,36 +19,41 @@
#include <iostream>
#include "search.h"
#include "thread.h"
#include "ucioption.h"
ThreadsManager Threads; // Global object definition
using namespace Search;
ThreadsManager Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID.
// There are two versions of this function; one for POSIX threads and
// one for Windows threads.
// is launched. It simply calls idle_loop() of the supplied thread. The first
// and last thread are special. First one is the main search thread while the
// last one mimics a timer, they run in main_loop() and timer_loop().
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID thread) {
#else
void* start_routine(void* thread) {
#endif
DWORD WINAPI start_routine(LPVOID threadID) {
Thread* th = (Thread*)thread;
if (th->threadID == 0)
th->main_loop();
else if (th->threadID == MAX_THREADS)
th->timer_loop();
else
th->idle_loop(NULL);
Threads.idle_loop(*(int*)threadID, NULL);
return 0;
}
#else
void* start_routine(void* threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
#endif
} }
@@ -63,15 +68,15 @@ void Thread::wake_up() {
}
// cutoff_occurred() checks whether a beta cutoff has occurred in
// the thread's currently active split point, or in some ancestor of
// the current split point.
// cutoff_occurred() checks whether a beta cutoff has occurred in the current
// active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
return true;
return false;
}
@@ -85,7 +90,7 @@ bool Thread::cutoff_occurred() const {
bool Thread::is_available_to(int master) const {
if (state != AVAILABLE)
if (is_searching)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
@@ -102,16 +107,41 @@ bool Thread::is_available_to(int master) const {
}
// read_uci_options() updates number of active threads and other internal
// parameters according to the UCI options values. It is called before
// to start a new search.
// read_uci_options() updates number of active threads and other parameters
// according to the UCI options values. It is called before to start a new search.
void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>();
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>();
activeThreads = Options["Threads"].value<int>();
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
set_size(Options["Threads"]);
}
// set_size() changes the number of active threads and raises do_sleep flag for
// all the unused threads that will go immediately to sleep.
void ThreadsManager::set_size(int cnt) {
assert(cnt > 0 && cnt <= MAX_THREADS);
activeThreads = cnt;
for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
if (i < activeThreads)
{
// Dynamically allocate pawn and material hash tables according to the
// number of active threads. This avoids preallocating memory for all
// possible threads if only few are used.
threads[i].pawnTable.init();
threads[i].materialTable.init();
threads[i].do_sleep = false;
}
else
threads[i].do_sleep = true;
}
@@ -120,22 +150,12 @@ void ThreadsManager::read_uci_options() {
void ThreadsManager::init() {
int threadID[MAX_THREADS];
// Initialize sleep condition and lock used by thread manager
cond_init(&sleepCond);
lock_init(&threadsLock);
// This flag is needed to properly end the threads when program exits
allThreadsShouldExit = false;
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
threads[0].state = Thread::SEARCHING;
// Allocate pawn and material hash tables for main thread
init_hash_tables();
lock_init(&mpLock);
// Initialize thread and split point locks
for (int i = 0; i < MAX_THREADS; i++)
// Initialize thread's sleep conditions and split point locks
for (int i = 0; i <= MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
@@ -144,48 +164,51 @@ void ThreadsManager::init() {
lock_init(&(threads[i].splitPoints[j].lock));
}
// Create and startup all the threads but the main that is already running
for (int i = 1; i < MAX_THREADS; i++)
// Allocate main thread tables to call evaluate() also when not searching
threads[0].pawnTable.init();
threads[0].materialTable.init();
// Create and launch all the threads, threads will go immediately to sleep
for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].state = Thread::INITIALIZING;
threadID[i] = i;
threads[i].is_searching = false;
threads[i].do_sleep = (i != 0); // Avoid a race with start_thinking()
threads[i].threadID = i;
#if defined(_MSC_VER)
bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL);
threads[i].handle = CreateThread(NULL, 0, start_routine, &threads[i], 0, NULL);
bool ok = (threads[i].handle != NULL);
#else
pthread_t pthreadID;
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
bool ok = !pthread_create(&threads[i].handle, NULL, start_routine, &threads[i]);
#endif
if (!ok)
{
std::cout << "Failed to create thread number " << i << std::endl;
std::cerr << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE);
}
// Wait until the thread has finished launching and is gone to sleep
while (threads[i].state == Thread::INITIALIZING) {}
}
}
// exit() is called to cleanly exit the threads when the program finishes
// exit() is called to cleanly terminate the threads when the program finishes
void ThreadsManager::exit() {
// Force the woken up threads to exit idle_loop() and hence terminate
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
for (int i = 0; i <= MAX_THREADS; i++)
{
// Wake up all the threads and waits for termination
if (i != 0)
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
threads[i].do_terminate = true; // Search must be already finished
threads[i].wake_up();
// Now we can safely destroy the locks and wait conditions
// Wait for thread termination
#if defined(_MSC_VER)
WaitForSingleObject(threads[i].handle, INFINITE);
CloseHandle(threads[i].handle);
#else
pthread_join(threads[i].handle, NULL);
#endif
// Now we can safely destroy associated locks and wait conditions
lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond);
@@ -193,58 +216,56 @@ void ThreadsManager::exit() {
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&mpLock);
}
// init_hash_tables() dynamically allocates pawn and material hash tables
// according to the number of active threads. This avoids preallocating
// memory for all possible threads if only few are used as, for instance,
// on mobile devices where memory is scarce and allocating for MAX_THREADS
// threads could even result in a crash.
void ThreadsManager::init_hash_tables() {
for (int i = 0; i < activeThreads; i++)
{
threads[i].pawnTable.init();
threads[i].materialTable.init();
}
lock_destroy(&threadsLock);
cond_destroy(&sleepCond);
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master".
// a slave for the thread with threadID 'master'.
bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++)
if (i != master && threads[i].is_available_to(master))
if (threads[i].is_available_to(master))
return true;
return false;
}
// split_point_finished() checks if all the slave threads of a given split
// point have finished searching.
bool ThreadsManager::split_point_finished(SplitPoint* sp) const {
for (int i = 0; i < activeThreads; i++)
if (sp->is_slave[i])
return false;
return true;
}
// split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
// split point objects), the function immediately returns. If splitting is
// possible, a SplitPoint object is initialized with all the data that must be
// copied to the helper threads and we tell our helper threads that they have
// been assigned work. This will cause them to instantly leave their idle loops and
// call search().When all threads have returned from search() then split() returns.
// several available threads. If it does not succeed in splitting the node
// (because no idle threads are available, or because we have no unused split
// point objects), the function immediately returns. If splitting is possible, a
// SplitPoint object is initialized with all the data that must be copied to the
// helper threads and then helper threads are told that they have been assigned
// work. This will cause them to instantly leave their idle loops and call
// search(). When all threads have returned from search() then split() returns.
template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta,
Value* bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, bool pvNode) {
assert(pos.is_ok());
assert(*bestValue >= -VALUE_INFINITE);
assert(*bestValue <= *alpha);
assert(*alpha < beta);
Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, int nodeType) {
assert(pos.pos_is_ok());
assert(bestValue > -VALUE_INFINITE);
assert(bestValue <= alpha);
assert(alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
@@ -253,93 +274,229 @@ void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const V
int i, master = pos.thread();
Thread& masterThread = threads[master];
lock_grab(&mpLock);
// If we already have too many active split points, don't split
if (masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
return bestValue;
// If no other thread is available to help us, or if we have too many
// active split points, don't split.
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
return;
}
// Pick the next available split point from the split point stack
SplitPoint* sp = &masterThread.splitPoints[masterThread.activeSplitPoints];
// Pick the next available split point object from the split point stack
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// Initialize the split point
sp->parent = masterThread.splitPoint;
sp->master = master;
sp->is_betaCutoff = false;
sp->depth = depth;
sp->threatMove = threatMove;
sp->alpha = alpha;
sp->beta = beta;
sp->nodeType = nodeType;
sp->bestValue = bestValue;
sp->mp = mp;
sp->moveCount = moveCount;
sp->pos = &pos;
sp->nodes = 0;
sp->ss = ss;
// Initialize the split point object
splitPoint.parent = masterThread.splitPoint;
splitPoint.master = master;
splitPoint.is_betaCutoff = false;
splitPoint.depth = depth;
splitPoint.threatMove = threatMove;
splitPoint.alpha = *alpha;
splitPoint.beta = beta;
splitPoint.pvNode = pvNode;
splitPoint.bestValue = *bestValue;
splitPoint.mp = mp;
splitPoint.moveCount = moveCount;
splitPoint.pos = &pos;
splitPoint.nodes = 0;
splitPoint.ss = ss;
for (i = 0; i < activeThreads; i++)
splitPoint.is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
sp->is_slave[i] = false;
// If we are here it means we are not available
assert(masterThread.state != Thread::AVAILABLE);
assert(masterThread.is_searching);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
// Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
lock_grab(&threadsLock);
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (i != master && threads[i].is_available_to(master))
if (threads[i].is_available_to(master))
{
threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
splitPoint.is_slave[i] = true;
workersCnt++;
}
sp->is_slave[i] = true;
threads[i].splitPoint = sp;
assert(Fake || workersCnt > 1);
// This makes the slave to exit from idle_loop()
threads[i].is_searching = true;
// We can release the lock because slave threads are already booked and master is not available
lock_release(&mpLock);
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for (i = 0; i < activeThreads; i++)
if (i == master || splitPoint.is_slave[i])
{
assert(i == master || threads[i].state == Thread::BOOKED);
threads[i].state = Thread::WORKISWAITING; // This makes the slave to exit from idle_loop()
if (useSleepingThreads && i != master)
if (useSleepingThreads)
threads[i].wake_up();
}
// Everything is set up. The master thread enters the idle loop, from
// which it will instantly launch a search, because its state is
// THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point.
idle_loop(master, &splitPoint);
lock_release(&threadsLock);
// We failed to allocate even one slave, return
if (!Fake && workersCnt == 1)
return bestValue;
masterThread.splitPoint = sp;
masterThread.activeSplitPoints++;
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set.
// We pass the split point as a parameter to the idle loop, which means that
// the thread will return from the idle loop when all slaves have finished
// their work at this split point.
masterThread.idle_loop(sp);
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!masterThread.is_searching);
// We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return.
lock_grab(&mpLock);
// finished. Note that changing state and decreasing activeSplitPoints is done
// under lock protection to avoid a race with Thread::is_available_to().
lock_grab(&threadsLock);
*alpha = splitPoint.alpha;
*bestValue = splitPoint.bestValue;
masterThread.is_searching = true;
masterThread.activeSplitPoints--;
masterThread.splitPoint = splitPoint.parent;
pos.set_nodes_searched(pos.nodes_searched() + splitPoint.nodes);
lock_release(&mpLock);
lock_release(&threadsLock);
masterThread.splitPoint = sp->parent;
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
return sp->bestValue;
}
// Explicit template instantiations
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
template Value ThreadsManager::split<false>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
extern void check_time();
void Thread::timer_loop() {
while (!do_terminate)
{
lock_grab(&sleepLock);
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
lock_release(&sleepLock);
check_time();
}
}
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
// milliseconds. If msec is 0 then timer is stopped.
void ThreadsManager::set_timer(int msec) {
Thread& timer = threads[MAX_THREADS];
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
cond_signal(&timer.sleepCond); // Wake up and restart the timer
lock_release(&timer.sleepLock);
}
// Thread::main_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() {
while (true)
{
lock_grab(&sleepLock);
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_terminate)
{
cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
cond_wait(&sleepCond, &sleepLock);
}
is_searching = true;
lock_release(&sleepLock);
if (do_terminate)
return;
think(); // This is the search entry point
}
}
// ThreadsManager::start_thinking() is used by UI thread to wake up the main
// thread parked in main_loop() and starting a new search. If asyncMode is true
// then function returns immediately, otherwise caller is blocked waiting for
// the search to finish.
void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode) {
Thread& main = threads[0];
lock_grab(&main.sleepLock);
// Wait main thread has finished before to launch a new search
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
// Copy input arguments to initialize the search
RootPosition.copy(pos, 0);
Limits = limits;
SearchMoves = searchMoves;
// Reset signals before to start the new search
memset((void*)&Signals, 0, sizeof(Signals));
main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode)
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
// and to wait for the main thread finishing the search. Needed to wait exiting
// and terminate the threads after a 'quit' command.
void ThreadsManager::stop_thinking() {
Thread& main = threads[0];
Search::Signals.stop = true;
lock_grab(&main.sleepLock);
cond_signal(&main.sleepCond); // In case is waiting for stop or ponderhit
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
// is reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void ThreadsManager::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
Thread& main = threads[0];
lock_grab(&main.sleepLock);
while (!Signals.stop)
cond_wait(&main.sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "movepick.h"
#include "pawns.h"
#include "position.h"
#include "search.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
@@ -37,15 +38,15 @@ struct SplitPoint {
SplitPoint* parent;
const Position* pos;
Depth depth;
bool pvNode;
Value beta;
int nodeType;
int ply;
int master;
Move threatMove;
// Const pointers to shared data
MovePicker* mp;
SearchStack* ss;
Search::Stack* ss;
// Shared data
Lock lock;
@@ -58,42 +59,44 @@ struct SplitPoint {
};
/// Thread struct is used to keep together all the thread related stuff like locks,
/// state and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited and
/// we don't have to care about someone changing the entry under our feet.
/// Thread struct keeps together all the thread related stuff like locks, state
/// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet.
struct Thread {
enum ThreadState
{
INITIALIZING, // Thread is initializing itself
SEARCHING, // Thread is performing work
AVAILABLE, // Thread is waiting for work
BOOKED, // Other thread (master) has booked us as a slave
WORKISWAITING, // Master has ordered us to start
TERMINATED // We are quitting and thread is terminated
};
void wake_up();
bool cutoff_occurred() const;
bool is_available_to(int master) const;
void idle_loop(SplitPoint* sp);
void main_loop();
void timer_loop();
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int threadID;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
volatile ThreadState state;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_terminate;
#if defined(_MSC_VER)
HANDLE handle;
#else
pthread_t handle;
#endif
};
/// ThreadsManager class is used to handle all the threads related stuff like init,
/// starting, parking and, the most important, launching a slave thread at a split
/// point. All the access to shared thread data is done through this class.
/// ThreadsManager class handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
@@ -104,27 +107,34 @@ public:
Thread& operator[](int threadID) { return threads[threadID]; }
void init();
void exit();
void init_hash_tables();
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void set_size(int cnt) { activeThreads = cnt; }
void set_size(int cnt);
void read_uci_options();
bool available_slave_exists(int master) const;
void idle_loop(int threadID, SplitPoint* sp);
bool split_point_finished(SplitPoint* sp) const;
void set_timer(int msec);
void wait_for_stop_or_ponderhit();
void stop_thinking();
void start_thinking(const Position& pos, const Search::LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode);
template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode);
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
Lock mpLock;
friend struct Thread;
Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
Lock threadsLock;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
int activeThreads;
volatile bool allThreadsShouldExit;
Thread threads[MAX_THREADS];
bool useSleepingThreads;
WaitCondition sleepCond;
};
extern ThreadsManager Threads;

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
*/
#include <cmath>
#include <algorithm>
#include "misc.h"
#include "search.h"
@@ -64,7 +65,7 @@ namespace {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[Min(ply, 511)]; }
int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; }
/// Function Prototypes
@@ -72,18 +73,18 @@ namespace {
enum TimeType { OptimumTime, MaxTime };
template<TimeType>
int remaining(int myTime, int movesToGo, int currentPly);
int remaining(int myTime, int movesToGo, int fullMoveNumber);
}
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(const SearchLimits& limits, int currentPly)
void TimeManager::init(const Search::LimitsType& limits, int currentPly)
{
/* We support four different kind of time controls:
@@ -103,10 +104,10 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
int hypMTG, hypMyTime, t1, t2;
// Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>();
int emergencyBaseTime = Options["Emergency Base Time"].value<int>();
int emergencyMoveTime = Options["Emergency Move Time"].value<int>();
int minThinkingTime = Options["Minimum Thinking Time"].value<int>();
int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"];
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
@@ -114,28 +115,28 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? Min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? std::min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * Min(hypMTG, emergencyMoveHorizon);
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = Max(hypMyTime, 0);
hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2);
optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = std::min(maximumSearchTime, t2);
}
if (Options["Ponder"].value<bool>())
if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime);
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
}
@@ -156,6 +157,6 @@ namespace {
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
return int(floor(myTime * Min(ratio1, ratio2)));
return int(floor(myTime * std::min(ratio1, ratio2)));
}
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,12 +20,12 @@
#if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED
struct SearchLimits;
/// The TimeManager class computes the optimal time to think depending on the
/// maximum available time, the move game number and other parameters.
class TimeManager {
public:
void init(const SearchLimits& limits, int currentPly);
void init(const Search::LimitsType& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring>
#include <iostream>
@@ -60,7 +59,7 @@ void TranspositionTable::set_size(size_t mbSize) {
if (!entries)
{
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#include <iostream>
#include "move.h"
#include "misc.h"
#include "types.h"

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,15 +20,35 @@
#if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get
/// started type 'make help'.
///
/// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Use always.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
///
/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW)
/// | Locks and Condition Variables: these are not supported by
/// | Windows XP and older, to compile for those platforms you
/// | should enable OLD_LOCKS.
#include <climits>
#include <cstdlib>
#if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
@@ -41,125 +61,111 @@ typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h>
# include <inttypes.h>
#endif
#define Min(x, y) (((x) < (y)) ? (x) : (y))
#define Max(x, y) (((x) < (y)) ? (y) : (x))
////
//// Configuration
////
//// For Linux and OSX configuration is done automatically using Makefile.
//// To get started type "make help".
////
//// For windows part of the configuration is detected automatically, but
//// some switches need to be set manually:
////
//// -DNDEBUG | Disable debugging mode. Use always.
////
//// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want the
//// | executable to run on some very old machines.
////
//// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction.
//// | Works only in 64-bit mode. For compiling requires hardware
//// | with popcnt support. Around 4% speed-up.
////
//// -DOLD_LOCKS | By default under Windows are used the fast Slim Reader/Writer (SRW)
//// | Locks and Condition Variables: these are not supported by Windows XP
//// | and older, to compile for those platforms you should enable OLD_LOCKS.
// Automatic detection for 64-bit under Windows
#if defined(_WIN64)
#define IS_64BIT
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#endif
// Automatic detection for use of bsfq asm-instruction under Windows
#if defined(_WIN64)
#define USE_BSFQ
#endif
// Intel header for _mm_popcnt_u64() intrinsic
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
#include <nmmintrin.h>
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64))
# define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif
// Define a __cpuid() function for gcc compilers, for Intel and MSVC
// is already available as an intrinsic.
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
inline void __cpuid(int CPUInfo[4], int InfoType)
{
int* eax = CPUInfo + 0;
int* ebx = CPUInfo + 1;
int* ecx = CPUInfo + 2;
int* edx = CPUInfo + 3;
*eax = InfoType;
*ecx = 0;
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx));
}
#else
inline void __cpuid(int CPUInfo[4], int)
{
CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0;
}
#endif
// Define FORCE_INLINE macro to force inlining overriding compiler choice
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
# define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline))
# define FORCE_INLINE inline __attribute__((always_inline))
#else
#define FORCE_INLINE inline
# define FORCE_INLINE inline
#endif
/// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
/// CpuHasPOPCNT is a global constant initialized at startup that
/// is set to true if CPU on which application runs supports popcnt
/// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt();
const bool HasPopCnt = true;
#else
const bool CpuHasPOPCNT = false;
const bool HasPopCnt = false;
#endif
/// CpuIs64Bit is a global constant initialized at compile time that
/// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT)
const bool CpuIs64Bit = true;
const bool Is64Bit = true;
#else
const bool CpuIs64Bit = false;
const bool Is64Bit = false;
#endif
#include <string>
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
const int MAX_MOVES = 256;
const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
enum CastleRight {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum ValueType {
VALUE_TYPE_NONE = 0,
@@ -176,34 +182,36 @@ enum Value {
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
enum PieceType {
PIECE_TYPE_NONE = 0,
NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
};
enum Color {
WHITE, BLACK, COLOR_NONE
WHITE, BLACK, NO_COLOR
};
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -4 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
@@ -240,42 +248,54 @@ enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
};
enum SquareColor {
DARK, LIGHT
};
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value. Compiler
/// is free to choose the enum type as long as can keep its data,
/// so ensure Score to be an integer type.
/// Score enum keeps a midgame and an endgame value in a single integer (enum),
/// first LSB 16 bits are used to store endgame value, while upper bits are used
/// for midgame value. Compiler is free to choose the enum type as long as can
/// keep its data, so ensure Score to be an integer type.
enum Score {
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
#define ENABLE_OPERATORS_ON(T) \
inline T operator+ (const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator- (const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator* (int i, const T d) { return T(i * int(d)); } \
inline T operator* (const T d, int i) { return T(int(d) * i); } \
inline T operator/ (const T d, int i) { return T(int(d) / i); } \
inline T operator- (const T d) { return T(-int(d)); } \
inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \
inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \
inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \
inline void operator*= (T& d, int i) { d = T(int(d) * i); } \
inline void operator/= (T& d, int i) { d = T(int(d) / i); }
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// Extracting the signed lower and upper 16 bits it not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, although not 100% standard
/// compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) {
return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u));
}
#endif
#define ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \
inline T operator*(const T d, int i) { return T(int(d) * i); } \
inline T operator-(const T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \
inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; }
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \
inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; }
ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType)
@@ -286,44 +306,23 @@ ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank)
#undef ENABLE_OPERATORS_ON
/// Added operators for adding integers to a Value
inline Value operator+(Value v, int i) { return Value(int(v) + i); }
inline Value operator-(Value v, int i) { return Value(int(v) - i); }
// Extra operators for adding integers to a Value
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
ENABLE_SAFE_OPERATORS_ON(Score)
// Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is
// implementation defined and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
#endif
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
// Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
/// Only declared but not defined. We don't want to multiply two scores due to
/// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
// Remaining Score operators are standard
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); }
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); }
inline Score operator* (int i, const Score d) { return Score(i * int(d)); }
inline Score operator* (const Score d, int i) { return Score(int(d) * i); }
inline Score operator- (const Score d) { return Score(-int(d)); }
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; }
inline void operator-= (Score& d1, const Score d2) { d1 = d1 - d2; }
inline void operator*= (Score& d, int i) { d = Score(int(d) * i); }
inline void operator/= (Score& d, int i) { d = Score(int(d) / i); }
/// Division of a Score must be handled separately for each term
inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
@@ -336,135 +335,178 @@ const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
inline Value value_mate_in(int ply) {
extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
extern int SquareDistance[64][64];
inline Value mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value value_mated_in(int ply) {
inline Value mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt));
return Piece((c << 3) | pt);
}
inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7);
inline PieceType type_of(Piece p) {
return PieceType(p & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
inline Color color_of(Piece p) {
return Color(p >> 3);
}
inline Color opposite_color(Color c) {
return Color(int(c) ^ 1);
}
inline bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
inline bool piece_type_is_ok(PieceType pt) {
return pt >= PAWN && pt <= KING;
}
inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
}
inline char piece_type_to_char(PieceType pt) {
static const char ch[] = " PNBRQK";
return ch[pt];
inline Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) {
return Square((int(r) << 3) | int(f));
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ 56);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(int(r) ^ (int(c) * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, square_rank(s));
}
inline SquareColor square_color(Square s) {
return SquareColor(int(square_rank(s) + s) & 1);
}
inline bool opposite_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(square_file(s1) - square_file(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(square_rank(s1) - square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(square_file(s)), rank_to_char(square_rank(s)), 0 };
return std::string(ch);
}
inline bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
inline bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
return Square((r << 3) | f);
}
inline bool square_is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline File file_of(Square s) {
return File(s & 7);
}
inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) {
return Square(s ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(r ^ (c * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s));
}
inline bool opposite_colors(Square s1, Square s2) {
int s = s1 ^ s2;
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char piece_type_to_char(PieceType pt) {
return " PNBRQK"[pt];
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
inline Square from_sq(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square to_sq(Move m) {
return Square(m & 0x3F);
}
inline bool is_special(Move m) {
return m & (3 << 14);
}
inline bool is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int is_enpassant(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline PieceType promotion_piece_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
inline Move make_promotion(Square from, Square to, PieceType pt) {
return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ;
}
inline Move make_enpassant(Square from, Square to) {
return Move(to | (from << 6) | (2 << 14));
}
inline Move make_castle(Square from, Square to) {
return Move(to | (from << 6) | (3 << 14));
}
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
}
#include <string>
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 };
return ch;
}
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
#endif // !defined(TYPES_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,97 +17,112 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace std;
namespace {
// FEN string for the initial position
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// UCIParser is a class for parsing UCI input. The class
// is actually a string stream built on a given input string.
typedef istringstream UCIParser;
// Keep track of position keys along the setup moves (from start position to the
// position just before to start searching). This is needed by draw detection
// where, due to 50 moves rule, we need to check at most 100 plies back.
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
void set_option(UCIParser& up);
void set_position(Position& pos, UCIParser& up);
bool go(Position& pos, UCIParser& up);
void perft(Position& pos, UCIParser& up);
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
void go(Position& pos, istringstream& up);
void perft(Position& pos, istringstream& up);
}
/// execute_uci_command() takes a string as input, uses a UCIParser
/// object to parse this text string as a UCI command, and calls
/// the appropriate functions. In addition to the UCI commands,
/// the function also supports a few debug commands.
/// Wait for a command from the user, parse this text string as an UCI command,
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// commands, the function also supports a few debug commands.
bool execute_uci_command(const string& cmd) {
void uci_loop() {
static Position pos(StartPositionFEN, false, 0); // The root position
Position pos(StartFEN, false, 0); // The root position
string cmd, token;
UCIParser up(cmd);
string token;
up >> token; // operator>>() skips any whitespace
if (token == "quit")
return false;
if (token == "go")
return go(pos, up);
if (token == "ucinewgame")
pos.from_fen(StartPositionFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, up);
else if (token == "setoption")
set_option(up);
else if (token == "perft")
perft(pos, up);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
while (token != "quit")
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
if (!getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop")
Threads.stop_thinking();
else if (token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
Threads.stop_thinking();
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
pos.from_fen(StartFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "perft")
perft(pos, is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip_me();
else if (token == "eval")
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
}
else if (token == "key")
cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << endl;
else if (token == "uci")
cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
}
else if (token == "key")
cout << "key: " << hex << pos.get_key()
<< "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "uci")
cout << "id name " << engine_name()
<< "\nid author " << engine_authors()
<< "\n" << Options.print_all()
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
return true;
}
@@ -118,127 +133,127 @@ namespace {
// fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves").
void set_position(Position& pos, UCIParser& up) {
void set_position(Position& pos, istringstream& is) {
Move m;
string token, fen;
up >> token; // operator>>() skips any whitespace
is >> token;
if (token == "startpos")
{
pos.from_fen(StartPositionFEN, false);
up >> token; // Consume "moves" token if any
fen = StartFEN;
is >> token; // Consume "moves" token if any
}
else if (token == "fen")
{
while (up >> token && token != "moves")
while (is >> token && token != "moves")
fen += token + " ";
else
return;
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>());
}
else return;
pos.from_fen(fen, Options["UCI_Chess960"]);
// Parse move list (if any)
while (up >> token)
pos.do_setup_move(move_from_uci(pos, token));
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
{
pos.do_move(m, *SetupState);
// Increment pointer to StateRingBuf circular buffer
if (++SetupState - StateRingBuf >= 102)
SetupState = StateRingBuf;
}
}
// set_option() is called when engine receives the "setoption" UCI
// command. The function updates the corresponding UCI option ("name")
// to the given value ("value").
// set_option() is called when engine receives the "setoption" UCI command. The
// function updates the UCI option ("name") to the given value ("value").
void set_option(UCIParser& up) {
void set_option(istringstream& is) {
string token, name;
string value = "true"; // UCI buttons don't have a "value" field
string token, name, value;
up >> token; // Consume "name" token
up >> name; // Read option name
is >> token; // Consume "name" token
// Handle names with included spaces
while (up >> token && token != "value")
name += " " + token;
// Read option name (can contain spaces)
while (is >> token && token != "value")
name += string(" ", !name.empty()) + token;
up >> value; // Read option value
// Read option value (can contain spaces)
while (is >> token)
value += string(" ", !value.empty()) + token;
// Handle values with included spaces
while (up >> token)
value += " " + token;
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
if (!Options.count(name))
cout << "No such option: " << name << endl;
else if (value.empty()) // UCI buttons don't have a value
Options[name] = true;
else
Options[name] = value;
}
// go() is called when engine receives the "go" UCI command. The
// function sets the thinking time and other parameters from the input
// string, and then calls think(). Returns false if a quit command
// is received while thinking, true otherwise.
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts
// the main searching thread.
bool go(Position& pos, UCIParser& up) {
void go(Position& pos, istringstream& is) {
string token;
SearchLimits limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves;
Search::LimitsType limits;
std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token)
while (is >> token)
{
if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "wtime")
up >> time[WHITE];
is >> time[WHITE];
else if (token == "btime")
up >> time[BLACK];
is >> time[BLACK];
else if (token == "winc")
up >> inc[WHITE];
is >> inc[WHITE];
else if (token == "binc")
up >> inc[BLACK];
is >> inc[BLACK];
else if (token == "movestogo")
up >> limits.movesToGo;
is >> limits.movesToGo;
else if (token == "depth")
up >> limits.maxDepth;
is >> limits.maxDepth;
else if (token == "nodes")
up >> limits.maxNodes;
is >> limits.maxNodes;
else if (token == "movetime")
up >> limits.maxTime;
is >> limits.maxTime;
else if (token == "searchmoves")
while (up >> token)
*cur++ = move_from_uci(pos, token);
while (is >> token)
searchMoves.push_back(move_from_uci(pos, token));
}
*cur = MOVE_NONE;
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok());
return think(pos, limits, searchMoves);
Threads.start_thinking(pos, limits, searchMoves, true);
}
// perft() is called when engine receives the "perft" command.
// The function calls perft() passing the required search depth
// then prints counted leaf nodes and elapsed time.
// perft() is called when engine receives the "perft" command. The function
// calls perft() with the required search depth then prints counted leaf nodes
// and elapsed time.
void perft(Position& pos, UCIParser& up) {
void perft(Position& pos, istringstream& is) {
int depth, time;
int64_t n;
if (!(up >> depth))
if (!(is >> depth))
return;
time = get_system_time();
time = system_time();
n = perft(pos, depth * ONE_PLY);
int64_t n = Search::perft(pos, depth * ONE_PLY);
time = get_system_time() - time;
time = system_time() - time;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << time

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,8 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cctype>
#include <iostream>
#include <algorithm>
#include <sstream>
#include "misc.h"
@@ -26,145 +25,103 @@
#include "ucioption.h"
using std::string;
using std::cout;
using std::endl;
OptionsMap Options; // Global object
// Our case insensitive less() function as required by UCI protocol
/// Our case insensitive less() function as required by UCI protocol
static bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
int c1, c2;
size_t i = 0;
while (i < s1.size() && i < s2.size())
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
if (c1 != c2)
return c1 < c2;
}
return s1.size() < s2.size();
}
// stringify() converts a numeric value of type T to a std::string
template<typename T>
static string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
}
/// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
/// values and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
OptionsMap::OptionsMap() {
int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Minimum Split Depth"] = UCIOption(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(1, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
// Set some SMP parameters accordingly to the detected CPU count
UCIOption& thr = o["Threads"];
UCIOption& msd = o["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count());
if (cpu_count() >= 8)
msd.defaultValue = msd.currentValue = stringify(7);
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Min Split Depth"] = UCIOption(msd, 4, 7);
o["Max Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
}
/// OptionsMap::print_all() returns a string with all the UCI options in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
/// operator<<() is used to output all the UCI options in chronological insertion
/// order (the idx field) and in the format defined by the UCI protocol.
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
string OptionsMap::print_all() const {
std::stringstream s;
for (size_t i = 0; i <= size(); i++)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
for (size_t idx = 0; idx < om.size(); idx++)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx)
{
const UCIOption& o = it->second;
s << "\noption name " << it->first << " type " << o.type;
os << "\noption name " << it->first << " type " << o.type;
if (o.type != "button")
s << " default " << o.defaultValue;
os << " default " << o.defaultValue;
if (o.type == "spin")
s << " min " << o.minValue << " max " << o.maxValue;
os << " min " << o.min << " max " << o.max;
break;
}
return s.str();
return os;
}
/// Option class c'tors
/// UCIOption class c'tors
UCIOption::UCIOption(const char* def) : type("string"), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = def; }
UCIOption::UCIOption(const char* v) : type("string"), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = v; }
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = (def ? "true" : "false"); }
UCIOption::UCIOption(bool v, string t) : type(t), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size())
{ defaultValue = currentValue = stringify(def); }
UCIOption::UCIOption(int v, int minv, int maxv) : type("spin"), min(minv), max(maxv), idx(Options.size())
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// set_value() updates currentValue of the Option object. Normally it's up to
/// the GUI to check for option's limits, but we could receive the new value
/// directly from the user by teminal window. So let's check the bounds anyway.
/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
/// check for option's limits, but we could receive the new value directly from
/// the user by teminal window, so let's check the bounds anyway.
void UCIOption::set_value(const string& v) {
void UCIOption::operator=(const string& v) {
assert(!type.empty());
if (v.empty())
return;
if ((type == "check" || type == "button") != (v == "true" || v == "false"))
return;
if (type == "spin")
{
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = v;
if ( !v.empty()
&& (type == "check" || type == "button") == (v == "true" || v == "false")
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
currentValue = v;
}

View File

@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,61 +25,50 @@
#include <map>
#include <string>
struct OptionsMap;
/// UCIOption class implements an option as defined by UCI protocol
class UCIOption {
public:
UCIOption() {} // To be used in a std::map
UCIOption(const char* defaultValue);
UCIOption(bool defaultValue, std::string type = "check");
UCIOption(int defaultValue, int minValue, int maxValue);
UCIOption() {} // Required by std::map::operator[]
UCIOption(const char* v);
UCIOption(bool v, std::string type = "check");
UCIOption(int v, int min, int max);
void set_value(const std::string& v);
template<typename T> T value() const;
void operator=(const std::string& v);
void operator=(bool v) { *this = std::string(v ? "true" : "false"); }
operator int() const {
assert(type == "check" || type == "button" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
operator std::string() const {
assert(type == "string");
return currentValue;
}
private:
friend class OptionsMap;
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int minValue, maxValue;
int min, max;
size_t idx;
};
/// Custom comparator because UCI options should not be case sensitive
/// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public:
struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
OptionsMap();
std::string print_all() const;
};
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
extern OptionsMap Options;
/// Option::value() definition and specializations
template<typename T>
T UCIOption::value() const {
assert(type == "spin");
return T(atoi(currentValue.c_str()));
}
template<>
inline std::string UCIOption::value<std::string>() const {
assert(type == "string");
return currentValue;
}
template<>
inline bool UCIOption::value<bool>() const {
assert(type == "check" || type == "button");
return currentValue == "true";
}
#endif // !defined(UCIOPTION_H_INCLUDED)