Compare commits

..

599 Commits

Author SHA1 Message Date
Marco Costalba 3caeabf73b Stockfish 2.3.1
Stockfish bench signature is: 5423738
2012-09-22 00:20:44 +02:00
Gary Linscott fdbe8006e0 Bonus for rook/queen attacking pawns on same rank
Patch and tuning by Gary Linscott from an idea of Ryan Taker.

Double tested by Gary:

Wins: 3390 Losses: 2972 Draws: 11323
LOS: 99.999992%
ELO: 8.213465 +- 99%: 6.746506 95%: 5.124415
Win%: 51.181792 +- 99%: 0.969791 95%: 0.736740

And by me:

After 5612 games 1255  1085  3271 +11 ELO
2012-09-21 23:25:13 +02:00
Marco Costalba 09acdac56b Fix compile on 64 bits
Reported by Quocvuong82.

No functional change.
2012-09-20 19:25:27 +02:00
Marco Costalba e4a0482e43 Simplify BSFTable initialization
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-09-19 20:47:53 +02:00
Marco Costalba 22a5f91aa7 Fix crash under Chess 960
We have a crash with this position:

rkqbnnbr/pppppppp/8/8/8/8/PPPPPPPP/RKQBNNBR w HAha -

What happens is that even if we are castling QUEEN_SIDE,
in this case we have kfrom (B8) < kto (C8) so the loop
that checks for attackers runs forever leading to a crash.

The fix is to check for (kto > kfrom) instead of
Side == KING_SIDE, but this is slower in the normal case of
ortodhox chess, so rewrite generate_castle() to handle the
chess960 case as a template parameter and allow the compiler
to optimize out the comparison in case of normal chess.

Reported by Ray Banks.
2012-09-16 14:14:55 +02:00
Marco Costalba 9ce7469846 Rename class Book to PolyglotBook
And move struct BookEntry out of the header where it is
not needed.

No functional change.
2012-09-16 10:32:57 +02:00
Marco Costalba 4b7dbb3922 Fix KpsK endgame
Broken by commit a44c5cf4f7 of 3 /12 / 2011 that
was labeled "No functional change" because our 'bench'
test didn't triggered that particular endgame. Indeed
we need to run a specific bench on a set of endgames
position when touching endgame.cpp because normal bench
does not cover endgames properly.

Found by MSVC 2012 code analyzer.
2012-09-16 08:57:26 +02:00
Marco Costalba e0035e9ca9 Restore development version
No functional change.
2012-09-15 11:02:08 +02:00
Marco Costalba 0a18adb02a Stockfish 2.3
Stockfish bench signature is: 5416292
2012-09-15 10:56:17 +02:00
Marco Costalba 630b3b2482 Fix compile with Intel 13.0
It seems Intel is unable to properly workout templates with 'static'
storage specifier.

Workaround using an anonymous namespace instead.

No functional change.
2012-09-15 10:55:39 +02:00
Marco Costalba 6008f6538e Don't exit earlier from aspiration window loop
Currently we exit the loop when

abs(bestValue) >= VALUE_KNOWN_WIN

but there is no logical reason for this. It seems more
natural to re-search again with full open window.

This has practically no impact in most cases, we have a
'no functional change' running 'bench' command.
2012-09-14 10:05:46 +02:00
Marco Costalba afcee1e8a4 Fix MSVC 2012 64bits warnings
Reported by Rein.

No functional change.
2012-09-14 09:57:13 +02:00
Marco Costalba e0bd0f250b Speed-up generate<LEGAL>
The trick here is to check for legality only in the
(rare) cases we have pinned pieces or a king move
or an en-passant.

This trick is able to increase the speed of perft
of more then 20%!

No functional change.
2012-09-11 20:24:31 +02:00
Marco Costalba 1598a3edf8 Remove redundancy in move generation
Introduce generate_all_moves() and remove a good
bunch of redundant code.

No functional change.
2012-09-09 17:05:02 +02:00
Marco Costalba 0dacab65eb Simplify generate_castle()
Skipping the calls to std::min(), std::man() we get
even a nice speed-up on perft.

No functional change.
2012-09-09 11:50:28 +02:00
Marco Costalba 834bd9edd7 Rename *last to *end
It is a more correct name because it points past the
last move of the list.

No functional change.
2012-09-09 10:24:40 +02:00
Marco Costalba 6e840f8033 Enable link time optimization only when optimizing
Because it is quite slow, skip it when 'optimize' flag is 'no'

No functional change.
2012-09-09 10:02:11 +02:00
Marco Costalba 2379312028 Revert "Simplify Option c'tor"
std::to_string() is C++11 material, not c++03.

So revert the patch.
2012-09-07 15:21:50 +02:00
Marco Costalba 37db62b2ea Simplify Option c'tor
No functional change.
2012-09-06 18:18:13 +02:00
Marco Costalba b50ce5ebfb Get rid of struct Time
We just need the milliseconds of current system
time for our needs. This allows to simplify the
API.

No functional change.
2012-09-04 09:38:51 +02:00
Marco Costalba 5900ab76a0 Rename current_time() to now()
Follow C++11 naming conventions.

No functional change.
2012-09-02 17:04:22 +02:00
Marco Costalba e6d8e74152 Greatly speed up SEE
Simply reshuffling the code inverting the condition in next_attacker()
yields a miraculous speed up of more than 3% under gcc!

On my laptop a bench run goes from 320Knps to 330Knps

No functional change.
2012-09-02 00:27:53 +02:00
Marco Costalba 9cdca7516c Unroll least valuable attacker loop in SEE
This allows to reduce the scanning for new X-ray attacks
according to the capturing piece type.

It seems to be just a very small speed increase in MSVC 64
bit and gcc 32 bit, I guess cache issues value more than some
instruction less to execute (as usual).

No functional change.
2012-09-01 18:57:38 +02:00
Marco Costalba 6436e75858 Slightly simplify SEE
Some renaming and small code reshuffle.

No fuctional change.
2012-09-01 15:42:04 +02:00
Marco Costalba 831f91b859 Retire Time::restart()
Simplify API.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-31 19:47:07 +02:00
Marco Costalba 1258c7aabe Don't need to memset HashTable
Default c'tor Entry() already initializes
to zero all its POD members.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-31 19:47:00 +02:00
Marco Costalba 8dcb4bc3cc Terminate threads before to exit main()
It is very difficult and risky to assure
that a running thread doesn't access a global
variable. This is currently true, but could
change in the future and we don't want to rely
on code that works 'by accident'. The threads
are still running when ThreadPool destructor is
called (after main() returns) and this could
lead to crashes if a thread accesses a global
that has been already freed. The solution is to
use an exit() function and call it while we are
still in main(), ensuring global variables are
still alive at threads termination time.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-29 19:11:44 +02:00
Marco Costalba 0a003d3ba1 Convert to sync_cout and sync_endl
Serialize access to std::cout all over the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-29 19:11:44 +02:00
Marco Costalba 92e759a676 Introduce serialization of accesses to std::cout
When many threds concurrently print you need to serialize
the access to std::cout to avoid output lines are intermixed
with the contents of each thread.

This is not strictly needed at the moment because
only main thread prints out, although some ad-hoc
test could trigger UCI::loop() printing while searching.

Anyhow we want to lift this pretty avoidable constrain
also as a prerequisite for future work.

This patch just introduces the support, next one will enable
the serialization.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-29 19:11:31 +02:00
Marco Costalba 3df2c01b57 Correctly handle handover of setup states
Before the search we setup the starting position doing all the
moves (sent by GUI) from start position to the position just
before to start searching.

To do this we use a set of StateInfo records used by each
do_move() call. These records shall be kept valid during all
the search because repetition draw detection uses them to back
track all the earlier positions keys. The problem is that, while
searching, the GUI could send another 'position' command, this
calls set_position() that clears the states! Of course a crash
follows shortly.

Before searching all the relevant parameters are copied in
start_searching() just for this reason: to fully detach data
accessed during the search from the UCI protocol handling.
So the natural solution would be to copy also the setup states.
Unfortunatly this approach does not work because StateInfo
contains a pointer to the previous record, so naively copying and
then freeing the original memory leads to a crash.

That's why we use two std::auto_ptr (one belonging to UCI and another
to Search) to safely transfer ownership of the StateInfo records to
the search, after we have setup the root position.

As a nice side-effect all the possible memory leaks are magically
sorted out for us by std::auto_ptr semantic.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-27 19:17:02 +02:00
Marco Costalba 8991a6f005 Use std::deque to store setup states 2012-08-26 18:07:54 +02:00
Marco Costalba 16f380e5c1 Document PolyGlotRandoms[] offsets
Should be more clear from where the 'magic' numbers
come from.

Also bit of reformat while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-25 20:00:07 +01:00
Marco Costalba cbd7ce468c Explicitly use threads.size()
Instead of just size(). Although code is longer,
should be more immediate to understand when reading.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-24 13:41:29 +01:00
Marco Costalba b6883c872d Introduce struct Mutex and ConditionVariable
To mimics C++11 std::mutex and std::condition_variable,
also rename locks and condition variables to be more
uniform across the classes.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-24 12:30:36 +01:00
Marco Costalba fde0b9e701 Slightly microptimize SEE
Reduce of one instruction. It seems a tad faster on
the profiler now. Very slightly but anyhow it is a
code semplification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-24 09:30:50 +01:00
Marco Costalba 7a2825053e Use size_t as operator[] argument type
This better mimics std::vector::operator[] and
fixes a warning with MSVC 64bit.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-22 11:44:43 +01:00
Marco Costalba 4e619a13d6 Merge generate_direct_checks() in generate_moves()
Further reduce redundancy in move generation.
Veirifed no speed regression on MSVC, Clang and gcc.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-22 11:34:52 +01:00
Marco Costalba 0de9257610 Streamline generate_moves()
Greatly simplify these very performace critical functions.
Amazingly we don't have any speed regression actually under
MSVC we have the same assembly for generate_moves() !

In generate_direct_checks() 'target' is calculated only
once being a loop invariant.

On Clang there is even a slight speed up.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-22 09:23:03 +01:00
Marco Costalba b84af67f4c Reformat piece values arrays
And rename stuff while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-20 19:17:58 +01:00
Marco Costalba 7c8b7222f5 Move zobrist keys out of Position
Are used by Position but do not belong to that class,
there is only one instance of them (that's why were
defined as static), so move to a proper namespace instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-20 18:24:06 +01:00
Marco Costalba ec9038b7b4 Retire copy c'tor from class Position
Not needed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-20 14:44:30 +01:00
Marco Costalba ab65d3fd0e Prefer a reference to a pointer
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-20 07:54:38 +01:00
Marco Costalba e8b7109eff Use enums instead of constants for piece values
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-20 00:08:34 +01:00
Marco Costalba 0c6ed5929c Document De Bruijn sequences
Insted of raw magic numbers.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-19 23:01:48 +01:00
Marco Costalba 2f4a9a140a Avoid wake up master thread when useless
Check we are the last slave of the split point
before to wake up the master. This should avoid
spurious wakes up.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-19 23:01:47 +01:00
Marco Costalba dba1bc354a Simplify idle_loop() signature
We can detect the split point master also from within idle_loop,
so we can call the function without parameters and remove an
overloaded member hack in Thread class.

Note that we don't need to take a lock around curSplitPoint
when entering idle_loop() because if we are the master then
curSplitPoint cannot change under our feet (because is_searching
is set and so we cannot be reallocated), if we are a slave
we enter idle_loop() only upon Thread creation and in that case
is always splitPointsCnt == 0. This is true even in the very rare
case that curSplitPoint != NULL, if we have been already allocated
even before entering idle_loop().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-19 23:01:28 +01:00
Marco Costalba 4b19430103 Prefer size_t over int for array sizes
Align to standard library conventions.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-19 11:01:46 +01:00
Marco Costalba 7c1f8dbde9 Introduce namespace Bitbases
Let's continue this namespace galore...

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-18 13:08:12 +01:00
Marco Costalba 2c1ba2ab0d Introduce namespace UCI
Ater previous patch it comes naturally to take this
extra step.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-18 12:26:22 +01:00
Marco Costalba b011818917 Retire struct OptionsMap
Directly use the underlying std::map instead and avoid
a useless inheritance.

As a nice side-effect Options global object has now a
default c'tor avoiding possible issues with globals
initializations.

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-18 11:30:27 +01:00
Marco Costalba bc4de9edae Explicitly qualify STL functions
Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-18 11:00:56 +01:00
Marco Costalba 9de4ee6d32 Retire MovePickerExt struct
Templetize MovePicker::next_move() member function instead. It
is easier and we also avoid the forwarding of MovePicker() c'tor
arguments in the common case.

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-08-18 10:46:24 +01:00
Marco Costalba 90ec4a403a Guard against 'divide by zero' in bench
Also remove an useless cast.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-30 18:05:15 +01:00
Joseph R. Prostko b61ec33f22 Added Haiku-specific changes to Makefile
First change: If Haiku is host platform, change
installation prefix to /boot/common/bin

Second change: Only link in pthreads if Haiku isn't
host platform.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-30 06:29:46 +01:00
Marco Costalba 1f7b5d9a79 Fix UCI promotion move notation
Regression introduced by revision
f0db6a6c0b

Spotted by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-26 18:16:24 +01:00
Linus Arver 5c6ba81cf2 Readme.txt: more grammar/style fixes 2012-07-19 12:09:39 -07:00
Linus Arver 591adf564a Readme.txt: grammar/stylistic fixes 2012-07-18 16:46:51 -07:00
Marco Costalba f0db6a6c0b Fix regression in move_to_san()
Broken since commit 628808a113

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-15 09:22:00 +01:00
Marco Costalba 520e680278 Introduce notation.h
And group there all the formatting functions but
uci_pv() that requires access to search.cpp variables.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-15 08:29:07 +01:00
Marco Costalba abc6a0be2f Rewrite pv_info_xxx() signatures
Use the helpers to format the PV info but without
writing to output stream (file or cout). Message
formatting and sending are two logically different
task.

Incidentaly reintroduce the pretty_pv() name,
from Glaurung memories :-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-15 07:48:33 +01:00
Marco Costalba 9dbda6652e Include castle moves in 'dangerous' flag
Simplifies the code and seems more natural.

We have a very small fucntional change becuase now
at PV nodes castles are extended one ply anyhow.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-14 12:29:11 +01:00
Marco Costalba 5dc0df8435 Merge exclusion search conditions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-14 12:18:14 +01:00
Marco Costalba 6e5a334c95 Remove redundant condition in is_dangerous()
A pawn on 7th is always passed so retire
this redundant condition.

No funtional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-13 17:57:26 +01:00
Marco Costalba 6becc81446 Silence a MSVC warning in class Tie
With warning level 4 MSVC complains that a default
assignment operator could not be generated due to
member 'file' is a reference (warning C4512).

Use a pointer instead of a reference and move
struct Tie outisde class Logger while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-08 10:50:23 +01:00
Marco Costalba 6b5322ce00 Rename first_1 / last_1 in lsb / msb
It seems more accurate: lsb is clear while 'first
bit' depends from where you look at the bitboard.

And fix compile in case of 64 bits platforms that
do not use BSFQ intrinsics.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-08 09:36:40 +01:00
Marco Costalba 67d91dfd50 Use last_1() to compute new TT size
Transposition table consists of a power of 2
number of TTCluster entries.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-07 19:46:13 +01:00
Marco Costalba 089e54c7fd Revert to -O3 with Clang
Instead of -O4 option that does not work with both mingw and
Linux gcc (tested with Clang 3.1).

As reported by Reed Kotler:
Turns out that -O4 is not a valid option for clang unless you have
the proper gold linker and plugins built. That's because -O4 enables
LTO, which writes out bitcode files during the compile, and then loads
those and optimizes them during the link phase.

It requires a linker that supports LLVM's LTO. There is a plugin for
Gold available as part of LLVM.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-07 19:05:38 +01:00
Marco Costalba 0b3ffb54b7 Fix signedness warning in time_to_msec()
We have a signed integer here so let the return type
take in account that.

Found by Clang with -Weverything option.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-07 16:28:39 +01:00
Marco Costalba 775488340e More idiomatic signature for operator=()
Return a reference instead of void so to enable
chained assignments like

"p = q = Position(...);"

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-05 11:55:35 +01:00
Marco Costalba 7d2530873e Streamline null search reduction formula
Although a (little) functional change, we have no ELO change
but formula it is now more clear.

After 13019 games at 30"+0.05
Mod vs Orig 2075 - 2088 - 8856 ELO 0 (+- 3.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-07-05 11:48:17 +01:00
Marco Costalba 18505f1fc4 Clear transposition table on "ucinewgame"
It seems the standard behaviour as implemented
in most engines although UCI protocol does not
specify what to do upon "ucinewgame" command.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-30 08:00:48 +01:00
Marco Costalba dc88cd691f Templetize make_move() helpers
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-25 08:09:55 +01:00
Marco Costalba ebe8009aff Reduce indentation in UCIOption::operator=()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-25 00:14:42 +01:00
Marco Costalba 628808a113 Micro-optimize move_to_san()
Calculate the attacks only for the piece to disambiguate,
not for all.

Also some reformatting while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-24 12:38:29 +01:00
Marco Costalba dc7fd868f4 Use type_of() to categorize the moves
Needed to rename old MoveType (used in move generation)
to GenType.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-24 11:07:18 +01:00
Marco Costalba 7b4aa10708 Rename move.cpp to notation.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-24 09:46:31 +01:00
Marco Costalba 960a689769 Rename ThreadsManager to ThreadPool
It is a more standard naming convention.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-24 09:45:37 +01:00
Marco Costalba 5f5d056c8f Replace make_square() with operator|(File, Rank)
Be fancy :-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-23 09:17:54 +01:00
Marco Costalba 9c7e2c8f9d Coding style in move.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-23 08:34:07 +01:00
Marco Costalba 4f5680950a Add min pawn-king distance to endgame evaluation
At endgame time push the king near his pawns (actually
one of them).

Original idea is from Critter (although slightly different),
implementation is mine and is completely different from the
original, in particular it is different the algorithm to
compute the minimum distance from pawns.

After 19895 games at 15"+0.05
Mod vs Orig 3638 - 3248 - 13009 ELO +7

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-22 06:31:18 +01:00
Marco Costalba 9793fa1906 Calculate min distance between king and his pawns
Just added infrastructure.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-17 09:41:18 +01:00
Marco Costalba 0446fc85de Reformat pick_random() in magics calculation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-10 11:46:52 +01:00
Marco Costalba 764d3f44b6 Fix a compile error in opposite_colors()
Error is due to ambiguous overloading of operator^
because we have both the built-in operator^(int, int)
and the user defined operator^(Bitboard, Square).

This error does not trigger when using Makefile becuase,
due to luck, the user defined operator^(Bitboard, Square)
happens to be always defined _after_ opposite_colors() so
that compiler does not claim. But in case of Microsoft MSVC
we don't have a Makefile and the order of files compilation
is chosen by the compiler (in an unpredictable way). So it
could still happen that error is not detected (as in my case),
but in another case the order of compilation of the files could
be so that at some point both operator^ were defined before
opposite_colors() and this triggers the error.

The fix is much simpler than the explanation :-)

Reported by Quocvuong82.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-06-02 09:04:11 +01:00
Marco Costalba 0412f4a1ee Fix an issue when adding a book during the game
Currently when we fail to open a book file, for instance
if it doesn't exsist, we leave Book::open() with ifstream
failbit set. If then the book file is added, we correctly
open it at next attempt, but failbit is still set so that
after opening we exit because ifstream::good() returns false.

The fix is to reset failbit upon exiting.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-27 14:13:14 +01:00
Marco Costalba 6828325881 Retire PieceOffset[] in book.cpp
And calculate piece offset on the fly. Also
improve comments.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-27 13:46:55 +01:00
Marco Costalba 61a054b170 Fix a possible 'Division by zero'
In case a book entry has 'count' field set to 0
we crash. Spotted by Clang's static analyzer.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-26 23:17:22 +01:00
Marco Costalba 3361ad4242 Rename psq_score in ReducedStateInfo
So to be fully in sync with StateInfo, and move struct
to position.h, just below StateInfo.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-26 22:43:23 +01:00
Marco Costalba 6c9c6dd989 Fix book file regression
Revision 2aac860db3 of 27 / 4 / 2012
changed can_castle() signatures from bool to int and
this broke the code that calculates polyglot hash key
in book.cpp

Instead of directly fixing the code we prefer to change
castling rights definitions to align to the polyglot ones
(as we did in previous patch). After this step we can simply
take internal castle rights as they are and use them
directly to calculate polyglot book hash key, as we do
in this patch that fixes the regression.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-26 10:14:08 +01:00
Marco Costalba a358dfe934 Redefine enum CastleRight
To be aligned with PolyGlot book castle right definitions.

This will be used by next patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-26 10:03:35 +01:00
Marco Costalba 0ecc920a09 Add a known draw case in kpk bitbase generation
Early classify as known draws the positions
where white king is trapped on the rook file.

Suggested by Dan Honeycutt.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-19 08:45:16 +01:00
Marco Costalba f86182e791 Simplify Position::print()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-12 12:50:11 +01:00
Marco Costalba c19a6ef82d Revert "Don't split if reduced below min_split_depth"
After extensive testing (I was off line this week and let
the test go on) it seems this change is useless:

After 33968 games on a QUAD (4 threads) at 15"+0.05
Mod vs Orig 5425 - 5550 - 22993 ELO -1 (+-2.1)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-12 10:00:26 +01:00
Marco Costalba ecb84464f9 Improve previous patch
Only in case of promotion we care about an upper case
promotion piece char, so std::transform() is overkill
for the task.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-12 09:21:52 +01:00
Balint Pfliegel 1b2af05ea6 Junior promotion patch
Assumption: Junior sends promotions according to the side to move (ucase/lcase).
Fact: Stockfish generally handles promotion lcase.
Patch: Handling position fen input moves always with lcase promotions.

Ported back by Portfish. No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-12 00:04:17 +01:00
Marco Costalba caef319219 Fix compilation with Android NDK
It seems ADL lookup is broken with the STLPort library. Peter says:

The compiler is gcc 4.4.3, but I don't know how many patches they
have applied to it. I think gcc has had support for Koenig lookup
a long time. I think the problem is the type of the vector iterator.
For example, line 272 in search.cpp:

 if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove))

gives the error:

jni/stockfish/search.cpp:272: error: 'count' was not declared in this scope

Here RootMoves is:

 std::vector<RootMove> RootMoves;

If std::vector<T>::iterator is implemented as T*, then Koenig lookup
would fail because RootMove* is not in namespace std.

I compile with the stlport implementation of STL, which in its vector
class has:

 typedef value_type* iterator;

I'm not sure if this is allowed by the C++ standard. I did not find
anything that says the iterator type must belong to namespace std.
The consensus in this thread

http://compgroups.net/comp.lang.c++.moderated/argument-dependent-lookup/433395

is that the stlport iterator type is allowed.

Report and patch by Peter Osterlund.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-11 17:16:51 +01:00
Marco Costalba 2f47844c7c Simplify attacks_bb()
And some formatting while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-01 13:16:20 +01:00
Marco Costalba b9bc6e823f Change pos.pieces() argument order
Let first argument to be the 'color'. This allows to align
pos.pieces() signatures with piece_list(), piece_count() and
all the other functions where 'color' argument is passed as
first one.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-01 12:09:20 +01:00
Marco Costalba 5e90580088 Convert constants to decimal representation
Hex representation doesn't add any value in those cases.
Preserve hex representation where more self-documenting
for instance for binary masks values.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-05-01 11:37:12 +01:00
Marco Costalba ef0496ff40 Don't split if reduced depth is below min_split_depth
It seems to increase SMP performances. To note that
this patch goes in the opposite direction of "Active
reparenting" where we try to reparent an idle slave
as soon as possible. Instead here we prefer to keep
it idle instead of splitting on a shallow / near the
leaves node.

After 11550 games on a QUAD (4 threads) at 15"+0.05
Mod vs Orig 1972 - 1752 - 7826 ELO +6 (+-3.6)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-29 23:28:07 +01:00
Marco Costalba 96f4ab48d8 Increase optimization level of Clang
Set optimization level to 4 and get a 2.564% faster binary:

Stockfish (Clang, Level 4) bench:
$ make build ARCH=osx-x86-64 COMP=clang
(Clang does not support PGO)
Average of 4 trials:
Total time (ms): 5137.5
Nodes searched: 5631135
Nodes/second: 1096084.5

Stockfish (Clang, Level 3) bench:
$ make build ARCH=osx-x86-64 COMP=clang
(Clang does not support PGO)
Average of 4 trials:
Total time (ms): 5269.25
Nodes searched: 5631135
Nodes/second: 1068679.25

Stockfish (GCC, PGO) bench:
$ make profile-build ARCH=osx-x86-64
Average of 4 trials:
Total time (ms): 5286
Nodes searched: 5631135
Nodes/second: 1065292.25

Suggestion and performance tests by Daylen Yang.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-29 23:11:23 +01:00
Marco Costalba 39c08c17c5 Remove unreachable extension condition
A PvNode that givesCheck has been already granted
an extension of ONE_PLY in previous condition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-29 18:58:48 +01:00
Andy Duplain b93693b831 Add support for Mac clang compiler
Makefile modified to support the clang compiler under Mac.
This was tested using clang 4 under Mountain Lion, but should
also work fine under Lion and possibly under Snow Leopard.

It requires the 'Xcode 4.x Command Line Tools' to be installed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-29 11:47:02 +01:00
Marco Costalba cdfe43eb8f Proper indenting of multiple conditions
Triviality due to a boring saturday morning.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-28 11:10:51 +01:00
Marco Costalba 456f37b8ab Rename square_empty() to is_empty()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-28 10:54:33 +01:00
Marco Costalba e8d89ca5b0 Micro-optimize king evaluation
Reuse already calculated value, instead of calling
king_safety() again.

Patch suggested by Balint Pfliegel.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-28 10:37:55 +01:00
Marco Costalba 2aac860db3 Fix wrong condition in PawnEntry::king_safety()
Since revision 374c9e6b63
we use also castling information to calculate king safety.
So before to reuse the cached king safety score we have to
veify not only king position, but also castling rights are
the same of the pre-calculated ones.

This is a very subtle bug, found only becuase even after
previous patch, consecutives runs of 'bench' _still_ showed
different numbers. Pawn tables are not cleared between 'bench'
runs and in the second run a king safety score, previously
evaluated under some castling rights, was reused by another
position with different castling rights instead of being
recalculated from scratch.

Bug spotted and tracked down by Balint Pfliegel

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-27 10:59:18 +01:00
Marco Costalba 8b00e50cb7 Clear TT before running 'bench'
Now that we can call bench multiple times
from command prompt we need to ensure searched
nodes remain constant across different runs.

Spotted by Blint Pfliegel.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-26 19:37:30 +01:00
Marco Costalba be3b8f3ae9 Retire "Active reparenting"
After 6K games at 60" + 0.1 on QUAD with 4 threads
this implementation fails to show a measurable increase,
result is well within error bar.

Perhaps with 8 or more threads resut is better but we
don't have the hardware to test. So retire for now and
in case re-add in the future if it proves good on big
machines.

The only good news is that we don't have a regression and
implementation is stable and bug-free, so could be reused
somewhere in the future.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-22 17:52:31 +01:00
Marco Costalba ce159b16b9 Fix endless reaparenting loop
The check for detecting when a split point has all the
slaves still running is done with:

   slavesMask == allSlavesMask

When a thread reparents, slavesMask is increased, then, if
the same thread finishes, because there are no more moves,
slavesMask returns to original value and the above condition
returns to be true. So that the just finished thread immediately
reparents again with the same split point, then starts and
then immediately exits in a tight loop that ends only when a
second slave finishes, so that slavesMask decrements and the
condition becomes false. This gives a spurious and anomaly
high number of faked reparents.

With this patch, that rewrites the logic to avoid this pitfall,
the reparenting success rate drops to a more realistical 5-10%
for 4 threads case.

As a side effect note that now there is no more the limit of
maxThreadsPerSplitPoint when reparenting.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-17 18:51:49 +01:00
Marco Costalba 5392007a24 Improved cutoff check when reparenting
Check for a cutoff occurred also high in
the tree and not only at current split
point.

This avoids some more wasted reparenting.

No functional chnage.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-16 18:28:08 +01:00
Marco Costalba f59323b56a Use more_than_one() instead of single_bit()
It is more correct given what the function does. In
particular single_bit() returns true also in case of
empty bitboards.

Of course also the usual renaming while there :-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-14 09:51:59 +01:00
Marco Costalba 25a9b601b2 Reparent to latest
Instead of reparenting to oldest split point, try to reparent
to latest. The nice thing here is that we can use the YBWC
helpful master condition to allow the reparenting of a split
point master as long as is reparented to one of its slaves.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-14 07:20:31 +01:00
Marco Costalba c645aca199 Don't reparent if a cutoff is pending
And update master->splitPointsCnt under lock
protection. Not stricly necessary because
single_bit() condition takes care of false
positives anyhow, but it is a bit tricky and
moving under lock is the most natural thing
to do to avoid races with "reparenting".

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-12 21:17:52 +01:00
Marco Costalba 44432f67d7 Active Reparenting
In Young Brothers Wait Concept (YBWC) available slaves are
booked by the split point master, then start to search below
the assigned split point and, once finished, return in idle
state waiting to be booked by another master.

This patch introduces "Active Reparenting" so that when a
slave finishes its job on the assigned split point, instead
of passively waiting to be booked, searches a suitable active
split point and reprents itselfs to that split point. Then
immediately starts to search below the split point in exactly
the same way of the others split point's slaves. This reduces
to zero the time waiting in idle loop and should increase
scalability especially whit many (8 or more) cores.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-10 18:22:58 +01:00
Marco Costalba d66b765eb6 Sync compute_xxx implementations
Also refactored Position::pos_is_ok() while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-09 13:11:38 +01:00
Marco Costalba e72b93e44f Move Tempo to evaluation
Apart from the semplification it is now more clear that
the actual Tempo added was half of the indicated score.
This is becuase at start compute_psq_score() added half
Tempo unit and in do_move() white/black were respectively
adding/subtracting one Tempo unit.

Now we have directly halved Tempo constant and everything
is more clear.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-09 11:14:00 +01:00
Marco Costalba 68885f78f3 Micro-optimize do_castle_move()
Use the same tables update trick used in do_move().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-09 09:41:41 +01:00
Marco Costalba 4a310baae2 Disable book during analysis
It is still enabled during fixed limit search so to
use it during fixed depth/nodes/time matches.

Bug reported by Daylen.

No functional changes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-09 08:09:02 +01:00
Marco Costalba d549497144 Introduce make_castle_right() helper
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-08 17:19:49 +01:00
Marco Costalba e56342ed00 Shrink castlePath[] and castleRookSquare[] sizes
Shrinking from [16] to [2][2] is able to speedup
perft of start position of almost 5% !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-08 16:55:01 +01:00
Marco Costalba 0049d3f337 Reduce stack usage
Shrink dimensions of the biggest stack consumers arrays.
In particular movesSearched[] can be safely shrinked
without any impact on strenght or risk of crashing.
Also MAX_PLY can be reverted to 100 with almost no impact
so to limit search recursion and hence stack allocation.

A different case is for MAX_MOVES (used by Movepicker's
moves[]), because we know that do exsist some artificial
position with about 220 legal moves, so in those cases SF
will crash. Anyhow these cases are never found in games.
An open risk remains perft, especially run above handcrafted
positions.

This patch originates from a report by Daylen that found
SF crashing on his Mac OS X 10.7.3 while in deep analysys
on the following position:

8/3Q1pk1/5p2/4r3/5K2/8/8/8 w - - 0 1

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-08 08:46:04 +01:00
Marco Costalba e0cae4bef8 Fix 'bench' for Chess960 case
Now a fen file with Chess960 positions is
correctly parsed. But it is mandatory to set
"UCI_Chess960" option _before_ to call bench.

Note that this was not needed/possible before
adding the possibility to call 'bench' from
command prompt.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-07 14:07:30 +01:00
Marco Costalba 9546b79e20 Use bench to implement UI 'perft' command
Now that we can call bench on current position
we can directly use it to perform our perft.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-07 13:45:53 +01:00
Marco Costalba cc04a745e2 Teach 'bench' to run current position
Now that we can call bench from command prompt
has a sense to teach bench to run the current
set position. To do this is enough to call bench
with 'current' as fen source parameter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-07 12:08:34 +01:00
Marco Costalba ce5b972736 Don't need to wait after a "ponderhit"
It is enough to wake up main thread. This is
a better fix than d033d5e06a.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-07 11:47:37 +01:00
Marco Costalba f30f384757 Revert "Penalty for undefended rook"
After extensive test Gary says:

"So, after 16k games at 10"+1" on an i7, the undefended rook test
looks to be not good (albeit by a very small margin).
3063 - 3093 - 9844 (-1).

I doubt that is causing the regression, but even so, it looks like
it's not worth keeping, and we can go back to the simpler undefended
minors check."

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-07 11:34:22 +01:00
Marco Costalba 676b2c8435 Replace Position::copy()
With assignment operator. And fix Position::flip().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 19:42:45 +01:00
Marco Costalba c2fc80e5d1 Revert thread_local stuff
Unfortunatly accessing thread local variable
is much slower than object data (see previous
patch log msg), so we have to revert to old code
to avoid speed regression.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 18:47:55 +01:00
Marco Costalba b1f57e92ce Use thread_local compiler specifics
Much faster then pthread_getspecific() but still a
speed regression against the original code.

Following are the nps on a bench:

Position
454165
454838
455433

tls
441046
442767
442767

ms (Win)
450521
447510
451105

ms (pthread)
422115
422115
424276

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 18:03:15 +01:00
Marco Costalba bed4075580 Fix a (theoretical) race leading to a crash
After we release the SplitPoint lock the master, suppose
is main thread, can safely return and if a "quit" command
is pending, main thread exits and associated Thread object
is freed. So when we access master->is_searching a crash
occurs.

I have never found such a race that is of course very rare
becuase assumes that from lock releasing we go to sleep for
a time long enough for the main thread to end the search and
return. But you can never know, and anyhow a race is a race.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 15:30:41 +01:00
Marco Costalba 5a2d525048 Teach UI thread to use main thread resources
So to avoid a crash when setting the moves in
UCI "position startpos moves ...." command.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 15:05:52 +01:00
Marco Costalba e1919384a2 Don't store Thread info in Position
But use the newly introduced local storage
for this. A good code semplification and also
the correct way to go.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 14:36:45 +01:00
Marco Costalba 699f700162 Introduce thread local storage
Use thread local storage to store a pointer to the thread we
are running on. This will allow to remove thread info from
Position class.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 14:36:39 +01:00
Marco Costalba 797c960d20 Rewrite pop_1st_bit() to be endian independent
With this change sources are fully endianess
independent, so we can simplify the Makefile.

Somewhat surprisingly we don't have any speed
regression !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-06 12:38:27 +01:00
Marco Costalba 673bc5526f Use a Thread instead of an array index
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-04 12:12:08 +01:00
Marco Costalba 0439a79566 Big Position renaming
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-04 07:35:49 +01:00
Marco Costalba 37fa8adc2b Micro-optimize last_1() for 32bits
Verified assembly it is a bit simpler.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-04 06:54:48 +01:00
Marco Costalba 2f99de0c6c Fix bench with fen files regression
Erroneusly adds default positions to the fens
loaded from external file.

Bug introduced in adb71b8096

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-02 18:40:12 +01:00
Marco Costalba 7a8429d9f1 Simplify Endgames::probe()
With this API change we simplify both function and caller site.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-02 09:58:29 +01:00
Marco Costalba dda0fa1a43 Use polymorphism to resolve map() overloading
The 2 overload functions map() accept a pointer to
EndgameBase<Value> or a pointer to EndgameBase<ScaleFactor>.

Because Endgame<E> is derived from one of them we can
directly use a pointer to this class to resolve the
overload as is needed in Endgames::add().

Also made class Endgames fully parametrized and no more
hardcoded to the types (Value or ScaleFactor) of endgames
stored.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 16:16:51 +01:00
Marco Costalba 7eb6a488ad Use a std::vector to store searchMoves
A std::set (that is a rb_tree) seems really
overkill to store at most a handful of moves
and nothing in the common case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 12:45:43 +01:00
Marco Costalba 72641dcaae Retire platform specifics include in misc.cpp
Now that platform.h is included in types.h we
don't need this stuff anymore.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 12:00:15 +01:00
Marco Costalba 6e00aa6bae Better document square flipping helpers
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 11:30:54 +01:00
Marco Costalba 9bbd27a80f Introduce Bitboards namespace
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 11:01:13 +01:00
Marco Costalba adb71b8096 Process 'bench' also from SF prompt
It is possible to start with 'stockfish', then from
command prompt type 'bench' and SF will do what you expect.
Old behaviour is anyhow preserved. As a bonus we can now
start from command line any UCI command understood by
Stockfish. The difference is that after execution of a
command from arguments SF quits, while at the end of the
same command from prompt SF stays in UCI loop.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-04-01 10:06:30 +01:00
Marco Costalba 32c504076f Use std::vector to implement HashTable
Allows some code semplification and avoids directly
allocation and managing heap memory.

Also the usual renaming while there.

No functional change and no speed regression.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-31 19:07:11 +01:00
Marco Costalba 304deb5e83 Rename Materials and Pawns hash stuff
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-31 11:59:23 +01:00
Marco Costalba d84865eac3 Complete the renaming in Search::LimitsType
This completes the job started with revision
4124c94583.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-31 09:16:09 +01:00
Marco Costalba cc6c745b54 Reset search time as early as possible
In particualr before to wake up main thread that
could take some random time. Until we don't reset
search time we are not able to correctly track
the elapsed search time and this can be dangerous
under extreme time pressure.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-31 10:01:31 +01:00
Marco Costalba d033d5e06a Revert "Call wait_for_search_finished() only when quitting"
We need to wake up main thread if it is sleeping
waiting for stop or ponderhit, so we cannot skip
calling wait_for_search_finished().

Found by Othello1984.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-30 18:58:58 +01:00
Marco Costalba b0b9bb3462 Last touches to pawns shelter code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-29 21:14:15 +01:00
Marco Costalba 5e18b81e87 Fix an hang when max depth is reached
In this case SF stop searching and goes sleeping
waiting for a stop / ponderhit before to return
best move. So when a "stop" arrives we need to wake
up the main thread again.

Another regression introduced by 3aa471f2a9,
hopefully the last one.

Thanks to Otello1984 to reporting this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-29 20:04:27 +01:00
Marco Costalba 10e64e0509 Refactor pawns shelter and storm
Renamed stuff and added comments. The aim is to make more
readable, at least by me ;-) , this newly added part of code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-29 00:09:27 +01:00
Marco Costalba 76622342ec Restore MS1BTable[]
Incredible typo from my side!

The 2 tables are completely different, one counts 1s the
other returns the msb position. Even more incredible
the 'stockfish bench' command returns the same number
of nodes!!!

Spotted by Justin Blanchard.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-28 19:11:37 +01:00
Marco Costalba d6e3a40c81 Silently handle "ucinewgame" command
Avoid returning "Unknown command", it seems some
GUI are misguided by this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-28 18:24:02 +01:00
Marco Costalba 46a50cbf38 Replace MS1BTable[] with BitCount8Bit[]
We already have the necessary infrastructure
in place.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-28 14:08:19 +01:00
Marco Costalba cc2b3ece5c Merge pull request #11 from glinscott/squash
Add more detailed pawn shelter/storm evaluation

After 10670 games at 10"+0.05
Mod vs Orig 2277 - 1941 - 6452 ELO +11 !!!

The first real increase since 2.2.2, congratulations Gary !!!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-28 07:48:30 +01:00
Marco Costalba 22e294e044 Set do_sleep out of lock protection
Fixes a not so rare crash (once every 100 games)
newly introduced. Unfortunatly I am still not
able to figure out why :-(

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-27 19:27:20 +01:00
Marco Costalba 4124c94583 Use UCI names in Search::LimitsType
There is no need to "invent" different names
from the original UCI parameters.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-27 14:26:58 +01:00
Marco Costalba a56322fde8 Merge pull request #9 from glinscott/master
Penalty for undefended rook

Almost no change at longer TC, but perhaps there
is a tiny increase....

After 17522 games at 10"+0.05
Mod vs Orig 3064 - 2967 - 11491 ELO +2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-26 19:57:27 +01:00
Marco Costalba 3d0d0237c5 Simplify start_searching() signature
Retire the "sync" behaviour that now is up to
the caller to honour.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-26 18:59:01 +01:00
Marco Costalba d11a529904 Call wait_for_search_finished() only when quitting
When quitting we should avoid RootPosition to be
destroyed while threads are still running, leading
to a crash. In case of a "stop" or "ponderhit"
command there is no need for the UI thread to wait.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-26 18:44:30 +01:00
Marco Costalba 3aa471f2a9 Introduce and use wait_for_search_finished()
Helper function that allows us to simplify
the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-26 18:22:41 +01:00
Gary Linscott 374c9e6b63 Add more detailed pawn shelter/storm evaluation 2012-03-26 07:52:10 -04:00
Marco Costalba 32d3a07c67 Move ThreadsManager::exit() to d'tor
And add final touches to this long patch series.

All the series has been verified against regression with
20K games at fast TC.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-26 08:18:17 +01:00
Marco Costalba b978eb05dc Fix compile error with gcc
We have a clash with start_fn defined both as a
Thread memeber and as a function pointer type in
pthread_create().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 23:08:35 +01:00
Marco Costalba e4efc8b741 Reset Thread::maxPly before a new search
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 16:44:55 +01:00
Marco Costalba 58c2fe391d Fix race in ThreadsManager::sleep()
We cannot set do_sleep flag of main thread before
"bestmove" is sent to GUI, otherwise GUI could send
immediately the next "go" command that triggers
start_thinking() and because do_sleep is set UI
thread resets the flag to launch a new search. But
when shortly after main thread returns to main_loop()
flag is incorrectly reset and main thread goes to sleep
hanging the engine.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 16:37:48 +01:00
Gary Linscott dbe5e28eaa Merge remote-tracking branch 'upstream/master' 2012-03-25 10:18:29 -04:00
Marco Costalba c483ffc773 Try to mimic std::thread API
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 12:43:19 +01:00
Marco Costalba 41561c9bb8 Use std::vector<Thread*> to store threads
We store pointers instead of Thread objects because
Thread is not copy-constructible nor copy-assignable
and default ones are not suitable. So we cannot store
directly in a std::vector.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 10:23:52 +01:00
Marco Costalba 553655eb07 Refactor Thread class
Associate platform OS thread to the Thread class instead of
creating it from ThreadsManager.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 10:23:51 +01:00
Marco Costalba f01b53c374 Refactor ThreadsManager::set_size() functionality
Split the data allocation, now done (mostly once)
in read_uci_options(), from the wake up and sleeping
of the slave threads upon entering/exiting the search.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 10:23:49 +01:00
Marco Costalba 8ec421fa14 Revert "Don't sync with C library I/O buffers"
It seems is the cause of strange and rare hangs
reported by some users where Stockfish stops
responding to GUI. It is not clear why but for
the moment revert the patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 10:23:16 +01:00
Marco Costalba 11b0c7b44a Don't ceil cpu_count()
It is already done at calling site where it is
more appropiate.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-25 09:57:33 +01:00
Marco Costalba f8224fc7d3 Fix a MSVC warning
Not correct warning about use of an uninitialized
variable. The warning is not correct becuase we can
never reach the warned code when in SpNode, anyhow
the fix is simple.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-24 10:14:21 +01:00
Marco Costalba b356e0fae3 Rename lock.h to platform.h
And move some more platform specific code there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-24 10:05:17 +01:00
Marco Costalba 06f33ff1ee Remove last platform specific code form thread.cpp
A somewhat tricky function pointer cast allows us
to move the platform specifics to lock.h, the cast
is tricky because return type is not the same of the
casted function in Linux (for Windows return type is
a DWORD that is a long) but this should not be a
problem as long as the size is the same;

From: http://stackoverflow.com/questions/188839/function-pointer-cast-to-different-signature

"OpenSSL was only casting functions pointers to
other function types taking and returning the same
number of values of the same exact sizes, and this
(assuming you're not dealing with floating-point)
happens to be safe across all the platforms and
calling conventions I know of. However, anything
else is potentially unsafe."

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-23 19:28:20 +01:00
Marco Costalba c47a74ec62 Merge two loops in ThreadsManager::init()
In analogy with ThreadsManager::exit()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-22 22:40:04 +01:00
Marco Costalba e26d13bb31 Use a local copy of tte->value()
This should avoid some aliasing issues
with TT table access.

After 3913 games at 10"+0.05
Mod vs Orig 662 - 651 - 2600  ELO +0 (+- 6.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-22 19:32:30 +01:00
Gary Linscott f41a21fefd Penalty for undefended rook as well 2012-03-22 07:33:47 -04:00
mcostalba d4c9abb967 Merge pull request #8 from glinscott/master
Optimize undefended minor check. Little editing by
me, no change even at assembly level.

No regression after 8K games at fast TC on a 64bit CPU.
2012-03-22 07:49:11 +01:00
Gary Linscott d1e18fc7dd Optimize undefended minor check. 2012-03-21 08:19:21 -04:00
Gary Linscott 3c6a4bfbed Penalize undefended minors
Even if not under attack. This seems to be good
especially on openings.

After 12112 games at 10"+0.05
Mod vs Orig 2175 - 1997 - 7940 ELO +5 (+- 3.7)

[Patch series from Gary, little edited by me]

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-21 08:00:57 +01:00
Marco Costalba 3b7dbc4f6d Fix Logger under MSVC iostream libraries
We need splitted Tie classes because MSVC stream library
takes a lock on buffer both on reading and on writing and
this causes an hang because, while searching, the I/O
thread is locked on getline() and when main thread is
trying to std::cout() something it blocks on the same
lock waiting for I/O thread getting some input and
releasing the lock.

The solution is to use separated streambuf objects for
cin and cout.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-20 21:46:08 +01:00
Marco Costalba 17d1940278 Remove cruft from Logger class
A big code simplification and cruft removing, make
Logger class a singleton and fully self conteined.
Also add direction indicators (">>" and "<<") to
better differentiate input and output lines in the
log file.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-20 19:24:52 +01:00
Marco Costalba 258da28e79 Better on_change() argument name
Using "o" as a parameter with the on_xxx(const UICOption& o)
functions is a bit dangerous because of confusion with "0".

Suggested by Rein Halbersma.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-20 07:09:58 +01:00
Marco Costalba df80232495 Add also logging of std::cin
Some trial was needed to find the correct recipe but now
we log both stdin and stdout to file "io_log.txt".

Link http://spec.winprog.org/streams/ was very useful
to understand the details of iostreams implementation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-19 21:59:25 +01:00
Marco Costalba eb28a683bd Add (smart) logging facility
By means of "Use Debug Log" UCI option it is possible to toggle
the logging of std::cout to file "out.txt" while preserving
the usual output to stdout. There is zero overhead when logging
is disabled and we achieved this without changing a single line
of exsisting code, in particular we still use std::cout as usual.

The idea and part of the code comes from this article:
http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-18 23:10:24 +01:00
Marco Costalba 2dfc94e0b6 Show startup messages immediately
In particular before initialization. So that SF
seems more snappy at startup.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-18 23:10:18 +01:00
Marco Costalba 7bc3688714 Revert to byTypeBB[0] storing occupied squares
As it was in Glaurung times. Also rearranged order
so that byTypeBB[0] is accessed before byTypeBB[x]
to be more cache friendly. It seems there is even
a small speedup.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-18 12:02:23 +01:00
Marco Costalba fc3ea7365a Rename occupied_squares() to pieces()
Also some microoptimizations, were there from ages
but hidden: the renaming suddendly made them visible!

This is a good example of how better naming lets you write
better code. Naming is really a kind of black art!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-18 11:18:38 +01:00
Marco Costalba 55376219b7 UCI buttons don't need a value
Take advantage of this to further simplify the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-17 21:44:50 +01:00
Marco Costalba 9b26356347 Don't use "OwnBook" by default
Stick to UCI protocol that says:

* by default all the opening book handling is done by the GUI,
  but there is an option for the engine to use its own book
  ("OwnBook" option, see below)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-17 13:16:51 +01:00
Marco Costalba 7c8a8e038f Retire "ucinewgame" UCI option
UCI protocol it is not clear about what the engine
should be supposed to do when "ucinewgame" is
received. Stockfish simply sets the position to
start FEN, but it is redundant becuase the GUI always
resends the position after "ucinewgame" command, so
it seems we can safely ignore that command.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-17 11:24:19 +01:00
Marco Costalba ee838f56f7 Fix UCI 'button' options
When a button fires UCIOption::operator=() is called and from
there the on_change() function. Now it happens that in case of
a button the on_change() function resets option's value to
"false" triggering again UCIOption::operator=() that calls again
on_change() and so on in an endless loop that is experienced
by the user as an application hang.

Rework the button logic to fix the issue and also be more clear
about how button works.

Reported by several people working with Scid and tracked down
to the "Clear Hash" UCI button by Steven Atkinson.

Bug recently introduced by 2ef5b4066e.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-17 10:35:01 +01:00
Marco Costalba 9934b8ec31 Don't sync with C library I/O buffers
Now we are forced to just use C++ iostream becuase
buffers are independent and using C library functions
like printf() or scanf() could yield to issues.

Speed up of about 1%.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-12 19:34:19 +01:00
Marco Costalba 3dccdf5b83 Fix time_to_msec() precision
Result of t.time * 1000 should be a 64 bit value, not an int.

Bug reported by several users.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-10 19:38:56 +01:00
Marco Costalba 4220f191d8 Introduce Eval namespace
Wrap evaluation related stuff and reshuffle
a bit the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-07 07:39:01 +01:00
Marco Costalba 843a5961e1 Double pinner bonus
Fine tune newly introduced pinner bonus score:

After 34696 games at 2"+0.05
Mod vs Orig 7474 - 7087 - 20135 ELO +3 (+- 2.4)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-07 07:31:43 +01:00
Marco Costalba d8e56cbe54 Convert init of eval to async option
So to be done only once at startup and in the (unlikely)
cases that a relevant UCI parameter is changed, instead
of doing it at the beginning of each search.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-06 19:21:00 +01:00
Marco Costalba 2ef5b4066e Async UCI options actions
Introduce 'on change' actions that are triggered as soon as
an UCI option is changed by the GUI. This allows to set hash
size before to start the game, helpful especially on very fast
TC and big TT size.

As a side effect remove the 'button' type option, that now
is managed as a 'check' type.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-05 19:20:07 +01:00
Marco Costalba 482b5b7ece Use new Time class in timed_wait()
And simplify the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-05 19:18:46 +01:00
Marco Costalba 19540c9ee8 Introduce single_bit() helper
Self-documenting code instead of a tricky
bitwise tweak, not known by everybody.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-04 23:37:44 +01:00
Marco Costalba cf0561d31a Introduce pinning bonus
Add a bonus if a slider is pinning an enemy piece.
Idea from Critter.

After 27443 games at 2"+0.05
Mod vs Orig 5900 - 5518 - 16025 ELO +4 (+- 2.7)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-04 23:01:31 +01:00
Marco Costalba 161c6b025e Rewrite time measurement code
Introduce and use a new Time class designed after
QTime, from Qt framework. Should be a more clear and
self documented code.

As an added benefit we now use 64 bits internally to get
millisecs from system time. This avoids to wrap around
to 0 every 2^32 milliseconds, which is 49.71 days.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-03-03 18:01:39 +01:00
Marco Costalba b966adf103 Halve rook on open file bonus for endgame
After 42206 fast games TC 2"+0.05
Mod vs Orig 12871 - 16849 - 12486 ELO +3 (+- 2.6)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-29 06:20:48 +01:00
Marco Costalba c2a68708ef Fix a shift overflow warning
Visual Studio 11 is worried that shift result could
overflow an integer, this is impossible becuase max
value of the shift is 4, but compiler cannot know it.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-27 20:32:41 +01:00
Marco Costalba 34178205fc Micro-optmize castling moves
Pre compute castle path so to quickly test
for impeded rule.

This speeds up perft on starting position
of more than 2%.

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-27 19:44:04 +01:00
Marco Costalba 5bb766e826 Rename promotion_piece_type() to promotion_type()
Shorter and equally clear to understand.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-26 18:39:53 +01:00
Marco Costalba 96eefc4af6 Introduce another two (bitboard,square) operators
And simplify the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-26 18:39:40 +01:00
Marco Costalba 8751b18cf0 Fix MSVC warning on streampos to size_t conversion
Fix this warning with MSVC 64 bits:

warning C4244: '=' : conversion from 'std::streampos' to 'size_t',
possible loss of data

Point is that std::streampos could be negative, while size_t
is always non-negative.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-26 12:05:37 +01:00
Marco Costalba 2608b9249d Retire ss->bestMove
And introduce SPlitPoint bestMove to pass back the
best move after a split point.

This allow to define as const the search stack passed
to split.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-21 20:31:22 +01:00
Marco Costalba 43f84efa15 Don't update bestValue in check_is_dangerous()
It is a prerequisite for next patch and simplifies
the function. testing at ultra fast TC shows no
regression.

After 24302 games at 2"+0.05
Mod vs Orig 5122 - 5038 - 13872 ELO +1 (+- 2.9)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-21 20:29:05 +01:00
Marco Costalba ea5616785e Fix a wrong check in pos_is_ok()
Bug introduced by revision a44c5cf4f7
of 3/12/2011.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-20 19:34:43 +01:00
Marco Costalba 6a48325c49 Further simplify castling rights
Reverse the meaning of castleRightsMask[sq] so that now
is stored the castling right that will be removed in
case a move starts from or arrives to sq square. This
allows to simplify the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-20 19:20:13 +01:00
Marco Costalba 50edb7cd73 Spread usage of pos.piece_moved()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-19 12:20:29 +01:00
Marco Costalba 4aadd1e401 Retire empty_squares()
Use ~pos.occupied_squares() instead and avoid to
hide the ~ computation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-19 11:50:50 +01:00
Marco Costalba ec5b9994b5 Index en-passant zobrist keys by file
Instead of by square. This is a more conventional
approach, as reported also in:

http://chessprogramming.wikispaces.com/Zobrist+Hashing

We shrink zobEp[] from 64 to 8 keys at the cost of an extra
'and 7' at runtime to get the file out of the ep square.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-19 11:31:36 +01:00
Marco Costalba 3441e0075d Move some stuff out of lock protection in split()
We shouldn't need lock protection to increment
splitPointsCnt and set curSplitPoint of masterThread.

Anyhow because this code is very tricky and prone to
races bound the change in a single patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-19 10:32:38 +01:00
Marco Costalba 821e1c7233 Micro-optimize castleRights update
When updating castleRights in do_move() perform only one
64bit xor with zobCastle[] instead of two.

The trick here is to define zobCastle[] keys of composite
castling rights as a xor combination of the keys of the
single castling rights, instead of 16 independent keys.

Idea from Critter although implementation is different.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-19 10:04:49 +01:00
Marco Costalba 6088ac2108 Small renaming in Thread struct
Should be a bit more clear the meaning of the
single variables.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-18 10:57:00 +01:00
Marco Costalba d8349f9d0f Fix a race when extracting PV from TT
Because TT table is shared tte->move() could change
under our feet, in particular we could validate
tte->move() then the move is changed by another
thread and we call pos.do_move() with a move different
from the original validated one !

This leads to a very rare but reproducible crash once
every about 20K games.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-18 10:20:07 +01:00
Marco Costalba fd35d92c1e Increase MAX_PLY from 100 to 256
There is no need to limit the maximum ply searched to
100, with deep exclusion search extensions we could
reach it even with much smaller search depths.

The only drawback is an increase in stack usage, but
is limited mainly to id_loop(), in particular the
recursive search() functions are not affected.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-18 10:16:01 +01:00
Marco Costalba 3b906ffc27 Micro-optimize pop_1st_bit() for 32 bits
Small perft speed-up of 2% and also a code
simplification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-13 10:07:09 +01:00
Marco Costalba 7b4b65d7a9 Templetize sliding attacks
No functional change and no speed regression, it seems
to be even a bit faster on MSVC and gcc.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-13 09:15:55 +01:00
Marco Costalba 099b5e45e6 Speedup sliders attacks for 32bit CPU
Replace a 64 bit 'and' by two 32 bits ones and
use unsigned instead of int.

This simple patch increases perft speed of 6% on
my Intel Core 2 Duo !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-12 15:26:18 +01:00
Marco Costalba b1768c115c Don't wake up threads at the beginning of the search
But only when needed, after a split point. This behaviour
does not apply when useSleepingThreads is false, becuase
in this case threads are not woken up at split points so
must be already running.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-12 14:17:12 +01:00
Marco Costalba 86e159997c Don't reset 50-move counter after castling
Rule says should be reset only after a capture and/or
a pawn move.

This incredible bug was here since Glaurung times !

Spotted by Kiriakos.

No functional change in the test bench because we
don't reach the 50 moves limits.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-12 10:07:10 +01:00
mcostalba 576f0f6985 Merge pull request #3 from glinscott/b46bf29
Detect stalemate in evaluation
2012-02-05 21:34:32 -08:00
Gary Linscott b46bf2950f Simpler stalemate check. 2012-02-05 14:52:01 -05:00
Gary Linscott 0347339970 Detect stalemate in KXK endgames
Also, handle cases where there are 2 bishops of the same color.
2012-02-05 10:24:53 -05:00
Marco Costalba 40e939421f Add "Slow Mover" UCI parameter to adjust time management
With default value of 100 no change in regard of current
behaviour. Increasing the value makes SF to think a
longer time for each move. Decreasing the value makes SF
to move faster.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-04 10:41:09 +01:00
Marco Costalba b1cf1acb93 Move wait_for_stop_or_ponderhit() under Thread
This method belongs to Thread, not to ThreadsManager.

Reshuffle stuff in thread.cpp while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-03 17:33:09 +01:00
Marco Costalba c94cfebb7e Reduce lock contention in idle_loop
Release split point lock before to wake up
master thread. This seems to increase speed
in case "sleeping threads" are used:

After 7792 games with 4 threads at very fast TC (2"+0.05)
Mod vs Orig 1722 - 1627 - 4443 ELO +4 (+- 5.1)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-03 13:43:58 +01:00
Marco Costalba 57e942145c Fix an alignment warning with MSVC
The declared alignment is different from the one
in the definition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-02-03 13:14:57 +01:00
Marco Costalba 51e8efdab5 Fix subtle race with slave allocation
When allocating a slave we set both is_searching
and splitPoint under lock protection.

Unfortunatly the order in which the variables are
set is not defined. This article was very clarifying:

http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/

So when in idle loop we test for is_searching and then
access splitPoint, it could happen that splitPoint is still
not updated leading to a possible crash.

Fix the race lock protecting splitPoint access.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-31 20:19:25 +01:00
Marco Costalba df31398bb9 Fix bug in useless checks prune
With current code we could raise bestValue above beta,
not what is intended for.

Spotted by Richard Vida.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-31 20:17:37 +01:00
Marco Costalba a94fd3bbec Reformat kpk bitbase
Simplify and streamline the code. Verified all the
resulting bitbases are not changed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-30 19:19:47 +01:00
Marco Costalba 1a1742ac4f Don't log search info after a stop
Fix an issue where the log file stores an incorrect +0.00
eval after a search has been stopped.

Bug reported by Ajedrecista.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-29 11:25:02 +01:00
Marco Costalba a492a9dd07 Bitwise operator overloads between Bitboard and Square
Yes, we try to be fancy here ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-29 10:59:50 +01:00
Marco Costalba 875a8079bc Replace clear_bit() with xor_bit()
This allows to retire ClearMaskBB[] and use just
one SquareBB[] array to set and clear a bit.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-29 09:38:40 +01:00
Marco Costalba b76c04c097 Rename ValueType to Bound
It is a more conventional and common naming.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-27 19:49:38 +01:00
Marco Costalba f7b4983137 Do not require -lpthread when linking in mingw
With this we should compeltely remove the need
of installing third party POSIX threads library
when compiling with mingw-gcc under Windows.

Spotted by Trung Tu.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-27 19:41:24 +01:00
Marco Costalba eeef654cd7 Restore LMR depth limit
It is not clear the advantage and we don't want
to risk of introducing regressions on this
very critical parameter. So revert to old limit.

After 16003 games
Mod vs Orig 2496 - 2421 - 11086 ELO +1 (+-3)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-27 19:31:41 +01:00
Marco Costalba 7fb6fd2f55 Reformat threads code
Apart from some renaming the biggest change
is the retire of split_point_finished()
replaced by slavesMask flags. As a side
effect we now take also split point lock
when allocation available threads.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-27 19:24:22 +01:00
Marco Costalba a189a5f0c5 Use Windows threads library with mingw
Instead of Posix threads. This seems to fix time
losses of the gcc compiled version for Windows.
The patch replaces the MSVC specific _MSC_VER flag
with _WIN32 and _WIN64 that are defined both by
MSVC and mingw-gcc.

Workaround found by Jim Ablett.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-25 07:32:31 +01:00
Marco Costalba 24b25b4827 Order bad captures by MVV/LVA
Instead of by SEE. Almost no ELO change but it is
a bit easier and is a more natural choice given
that good captures are ordered in the same way.

After 10424 games
Mod vs Orig 1639 - 1604 - 7181 ELO +1 (+-3.8)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-24 07:12:19 +01:00
Marco Costalba 830ff985db Revert "Fix link time optimization gcc option"
It seems we need to pass the full optimization
flags to the linker otherwise we end up in a
slow compile:

http://lists.debian.org/debian-devel/2011/06/msg00181.html

Regression reported by Benigno Hernandez.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-23 20:52:21 +01:00
Marco Costalba 3d937e1e90 Simplify locking usage
pass references (Windows style) instead of
pointers (Posix style) as function arguments.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-23 20:30:19 +01:00
Marco Costalba 04ff9c2548 Simplify our insertion sort implementation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-23 20:24:15 +01:00
Auguste Pop 28bf56e725 Fix link time optimization gcc option
The previous line, LDFLAGS += $(CXXFLAGS), does not make sense, and
breaks profile-build, thus changing it into: LDFLAGS += -flto.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 21:58:42 +01:00
Marco Costalba 662d1859bd Shrink sequencer table
Integrate TT_MOVE step into the first state. This allows to
avoid the first call to next_phase() in case of a TT move.

And use overflow detection instead of the bunch of STOP_XX
states to detect end of moves.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 16:06:29 +01:00
Marco Costalba 97e0b0a01e Assorted code style in movepicker.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 13:57:24 +01:00
Marco Costalba 04ac1bcabe Fix incorrect assert(PvNode == (alpha != beta - 1))
In case of a PvNode could happen that alpha == beta - 1,
for instance in case the same previous node was visited
with same beta during a non-pv search, the node failed low
and stored beta-1 in TT. Then the node is searched again
in PV mode, TT value beta-1 is retrieved and updates alpha
that now happens to be beta-1.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 11:41:34 +01:00
Marco Costalba ec9b037e5f Order the recaptures by MVV/LVA
Almost no functional change because multiple recaptures
to same square are very rare, but neverthless it seems
the correct thing to do.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 11:19:44 +01:00
Marco Costalba 6f6be95bad Rename NON_CAPTURE to QUIET
It is a more conventional naming and is nicer.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 00:42:59 +01:00
Marco Costalba b96db269a8 Reshuffle stuff in MovePicker
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-22 00:13:47 +01:00
Marco Costalba dcbb05ef39 Fix ss->currentMove when probcutting
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-21 23:30:56 +01:00
Marco Costalba e25de55fac Use an enum instead of a table as MovePicker sequencer
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-21 16:05:54 +01:00
Marco Costalba 28892666bd Sync generate_direct_checks() with generate_piece_moves()
They are almost the same, if the function arguments would
have been the same they could even be integrated.

Also a bit of renaming while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-21 12:49:37 +01:00
Marco Costalba 6058e7cf60 Triviality in SERIALIZE_PAWNS() macro usage
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-18 17:52:55 +01:00
Justin Blanchard 2a21543c88 Remove unused #include lines 2012-01-19 00:48:53 +08:00
Justin Blanchard 007613cb5e Fix "go nodes", at least when Threads=1 2012-01-19 00:48:53 +08:00
Marco Costalba 1b69ef8e6c Don't allow LMR to fall in qsearch
And increase LMR limit. Tests show no change ELO wise,
but we prefer to take the risk to commit anyhow becuase
is a 'prune reducing' patch.

After 10749 games
Mod vs Orig: 1670 - 1676 - 7403 ELO 0 (+-3.7)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-18 08:10:40 +01:00
Marco Costalba 972dec454c Microptimize generation of pawn evasions
Skip calling promotion generation functions in
the very common case of no possible promotion
evasion. Also retire generate_pawn_captures()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-18 07:47:42 +01:00
Marco Costalba b6b8c62ba5 Simplify pawn captures generation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-16 19:29:15 +01:00
Marco Costalba db57b5f8f4 Fix a (bogus) warning with gcc 4.4
Fix an incorrect warning: 'bm may be used uninitialized'
with the old but still commonly used gcc 4.4

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-16 19:26:42 +01:00
Marco Costalba 4a4513d126 Retire double-profile-build Makefile target
Now that we don't support anymore popcount detection
at runtime this target is obsolete.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-15 17:02:06 +01:00
Marco Costalba 2b1324eddb Fix gcc name used in Link Time Optimization
Use $(CXX) instead of assuming compiler name is 'gcc'

Spotted by Louis Zulli.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-15 12:50:09 +01:00
Marco Costalba dfd030b67a Make init_magic() piece agnostic
All the piece dependant data is passed now as
function arguments so that the code is exactly
the same for bishop and rook.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-15 09:21:00 +01:00
Marco Costalba 20621ed3e3 Unify some template specializations
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 23:11:59 +01:00
Marco Costalba 2aec8fb956 Retire queen_attacks_bb()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 23:00:10 +01:00
Marco Costalba 3ec94abcdb Use 'adjacent' instead of 'neighboring'
It is more correct and specific. Another naming
improvement while reading Critter sources.

No functional changes.

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

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 15:27:24 +01:00
Marco Costalba f237e8b8ea Stockfish 2.2.2
Dedicated to the new Jim's super fast builds ! :-)

stockfish bench signature is: 5447426

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 15:01:21 +01:00
Marco Costalba 103b368ab7 Move struct RootMove to Search namespace
And directly pass RootMoves instead of SearchMoves
to main thread. A class declaration is better suited
in a header and slims a bit the fatty search.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 14:22:34 +01:00
Marco Costalba a29dd88f75 Use a set to store SearchMoves
We just need to verify if a legal move is among the
SearchMoves, so we don't need a vector for this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 13:06:01 +01:00
Marco Costalba a1076cc68a Fix a gcc 4.7 warning
New gcc 4.7 complains about casting a volatile pointer
to void* so assign the variables directly.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-13 07:58:21 +01:00
Marco Costalba 78908b7aed Enable easy move detection only for recaptures
It could lead to terrible mistakes otherwise, as
it happened during a game on playchess when on
this position (after white's f4):

2q4r/4b1k1/p3rpp1/3np2p/PpNpNP1P/1P1P2PQ/2P1R3/4R1K1 b - - 0 1

SF moves immediately e5xf4 instead of the correct f5.
In general during engine matches it is impossible the
opponent leaves a piece hanging or anyhow starts a
clear losing sequence. So avoid to fall in subtle traps.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-13 07:27:01 +01:00
Marco Costalba d98150dffc Use operator~ to flip colors and squares
More natural and nicer. Idea from Critter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-12 19:37:44 +01:00
Marco Costalba c549f71f64 Small touches in FEN decoding
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-12 18:40:58 +01:00
Marco Costalba c19ea4b000 Retire is_mate()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-10 21:53:07 +01:00
Marco Costalba bede30e7a6 Introduce piece_moved() to simplify common code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-09 22:36:45 +01:00
Marco Costalba b05fbb3733 Unify PseudoAttacks arrays
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-09 22:08:37 +01:00
Marco Costalba 9b43fd7937 Use CheckInfo to generate checks
It should help to avoid recalculating check squares
of sliding attackers for queen when already done for
bishops and rooks. Of course this helps when there are
bishop, rook and queen on the board !

Fixed also a subtle bug (use of same variable b in while
condition and in condition body) introduced recently by
revision d655147e8c that triggers
in case we have at least 2 non-pawn discovered check pieces.
This is very rare that's why didn't show in the node count
verification where we actually have a case of 2 dc pieces
in position 14, but one is a pawn.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-08 16:09:18 +01:00
Marco Costalba 87fc9dcaa3 Add castling to generation of checking moves
During generation of non-captures checks (in qsearch)
we don't consider castling moves that give check,
this patch includes also this rare case. Verified with
perft that all the non-capture checks are now generated.

There should be a very little slowdown due to the extra
work, but actually I failed to measure it. I don't expect
any ELO improvment, there is even no functional change on
the standard depth 12 search, it is just to have a correct
move generator.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-08 12:28:51 +01:00
Marco Costalba 83f3ea7ab4 Last touches to movegen.cpp
The full movegen patch series shows a speed up of almost
6% (!) on perft, and code is much more readable too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 13:13:28 +01:00
Marco Costalba 9c8c4ff46f Retire the redundant MV_CHECK
MV_CHECK is an alias of the more appropiate named
MV_NON_CAPTURE_CHECK so use only the latter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:19 +01:00
Marco Costalba d655147e8c Retire generate_discovered_checks
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:17 +01:00
Marco Costalba 3c675db3d0 Rearrange pawn moves generation
Functional change due only to rearrangement

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:16 +01:00
Marco Costalba cf247e7e30 Reshuffle stuff in movegen.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:15 +01:00
Marco Costalba 0026c88b3a Retire OLD_LOCKS option
And make CRITICAL_SECTION locks the only option for Windows.
This guarantees backward compatibility with all the Windows
versions (even XP and older) and an hassle free experience
when compiling for Windows. Tests performed by Ingo and
reported on talkchess confirm there is no speed penalty
against the most modern SRW locks:

http://www.talkchess.com/forum/viewtopic.php?t=41835&start=20

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 11:18:09 +01:00
Marco Costalba 6482ce2bb2 Fix compile on HP-UX 11's HP's C++
On that platform non-bracketed casting are not supported.

Reported by Richard Lloyd.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 00:04:08 +01:00
Marco Costalba 867a5a8cd2 Restore development version
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 18:09:25 +01:00
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
48 changed files with 6844 additions and 8222 deletions
+28 -35
View File
@@ -2,21 +2,21 @@
--------------- ---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI not a complete chess program and requires some UCI-compatible GUI
(like XBoard with PolyGlot, eboard, Josè, Arena, Sigma Chess, Shredder, (e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess
Chess Partner, or Fritz) in order to be used comfortably. Read the Partner or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use documentation for your GUI of choice for information about how to use
Stockfish with your GUI. Stockfish with it.
This version of Stockfish supports up to 32 CPUs, but has not been 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 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 number of CPUs on your computer and sets the number of search threads
accordingly, but please be aware that the detection is not always accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the correct. It is therefore recommended to inspect the value of the
"Threads" UCI parameter, and to make sure it equals the number of CPU "Threads" UCI parameter, and to make sure it equals the number of CPU
cores on your computer. If you are using more than four threads, it cores on your computer. If you are using more than eight threads, it is
is recommended to raise the value of "Minimum Split Depth" UCI parameter recommended to raise the value of the "Min Split Depth" UCI parameter to
to 6. 7.
2. Files 2. Files
@@ -26,13 +26,12 @@ This distribution of Stockfish consists of the following files:
* Readme.txt, the file you are currently reading. * Readme.txt, the file you are currently reading.
* Copying.txt, a text file containing the GNU General Public * Copying.txt, a text file containing the GNU General Public License.
License.
* src/, a subdirectory containing the full source code, including a * src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like Makefile that can be used to compile Stockfish on Unix-like systems.
systems. For further information about how to compile Stockfish For further information about how to compile Stockfish yourself read
yourself, read section 4 below. section 4 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot * polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter. adapter.
@@ -41,38 +40,33 @@ This distribution of Stockfish consists of the following files:
3. Opening books 3. Opening books
---------------- ----------------
This version of Stockfish has support for PolyGlot opening books. This version of Stockfish has support for PolyGlot opening books. For
For information about how to create such books, consult the PolyGlot information about how to create such books, consult the PolyGlot
documentation. The book file can be selected by setting the UCI documentation. The book file can be selected by setting the "Book File"
parameter "Book File". UCI parameter.
4. Compiling it yourself 4. Compiling it yourself
------------------------ ------------------------
On Unix-like systems, it should usually be possible to compile On Unix-like systems, it should be possible to compile Stockfish
Stockfish directly from the source code with the included Makefile. directly from the source code with the included Makefile.
For big-endian machines like Power PC you need to enable the proper Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile. instruction, big-endian machines such as Power PC, and other platforms.
Stockfish has POPCNT instruction runtime detection and support. This can In general it is recommended to run 'make help' to see a list of make
give an extra speed on Core i7 or similar systems. To enable this feature targets with corresponding descriptions. When not using Makefile to
compile with 'make icc-profile-popcnt' compile (for instance with Microsoft MSVC) you need to manually
set/unset some switches in the compiler command line; see file "types.h"
On 64 bit systems the 'bsfq' assembly instruction will be used for bit for a quick reference.
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
In general is recommended to run 'make help' to see a list of make targets
with corresponding descriptions.
5. Terms of use 5. Terms of use
--------------- ---------------
Stockfish is free, and distributed under the GNU General Public License Stockfish is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly (GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or it (either by itself or as part of some bigger software package), or
@@ -80,9 +74,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 The only real limitation is that whenever you distribute Stockfish in
some way, you must always include the full source code, or a pointer some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL. source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named For full details, read the copy of the GPL found in the file named
Copying.txt. Copying.txt.
+11 -7
View File
@@ -15,13 +15,10 @@ ResignScore = 600
[Engine] [Engine]
Hash = 128
Threads = 1
OwnBook = false
Book File = book.bin
Best Book Move = false
Use Search Log = false Use Search Log = false
Search Log Filename = SearchLog.txt Search Log Filename = SearchLog.txt
Book File = book.bin
Best Book Move = false
Mobility (Middle Game) = 100 Mobility (Middle Game) = 100
Mobility (Endgame) = 100 Mobility (Endgame) = 100
Passed Pawns (Middle Game) = 100 Passed Pawns (Middle Game) = 100
@@ -29,11 +26,18 @@ Passed Pawns (Endgame) = 100
Space = 100 Space = 100
Aggressiveness = 100 Aggressiveness = 100
Cowardice = 100 Cowardice = 100
Minimum Split Depth = 4 Min Split Depth = 4
Maximum Number of Threads per Split Point = 5 Max Threads per Split Point = 5
Threads = 1
Use Sleeping Threads = false Use Sleeping Threads = false
Hash = 128
Ponder = true
OwnBook = false
MultiPV = 1
Skill Level = 20 Skill Level = 20
Emergency Move Horizon = 40 Emergency Move Horizon = 40
Emergency Base Time = 200 Emergency Base Time = 200
Emergency Move Time = 70 Emergency Move Time = 70
Minimum Thinking Time = 20 Minimum Thinking Time = 20
UCI_Chess960 = false
UCI_AnalyseMode = false
+80 -91
View File
@@ -1,6 +1,6 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) # Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008-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 # Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -20,11 +20,18 @@
### Section 1. General Configuration ### Section 1. General Configuration
### ========================================================================== ### ==========================================================================
### Establish the operating system name
UNAME = $(shell uname)
### Executable name ### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions ### Installation dir definitions
PREFIX = /usr/local PREFIX = /usr/local
# Haiku has a non-standard filesystem layout
ifeq ($(UNAME),Haiku)
PREFIX=/boot/common
endif
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### Built-in benchmark for pgo-builds
@@ -32,7 +39,7 @@ PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files ### Object files
OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
material.o misc.o move.o movegen.o movepick.o pawns.o position.o \ material.o misc.o movegen.o movepick.o notation.o pawns.o position.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o search.o thread.o timeman.o tt.o uci.o ucioption.o
### ========================================================================== ### ==========================================================================
@@ -42,16 +49,15 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
# flag --- Comp switch --- Description # 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 # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture # arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system # os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = no/yes --- -DBIGENDIAN --- big/little-endian byte order # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# prefetch = no/yes --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction # bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only
# bsfq = no/yes --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction # with GCC and ICC 64-bit)
# --- (Works only with GCC and ICC 64-bit) # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# popcnt = no/yes --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# #
# Note that Makefile is space sensitive, so when adding new architectures # 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 # or modifying existing flags, you have to make sure there are no extra spaces
@@ -68,7 +74,6 @@ ifeq ($(ARCH),general-64)
arch = any arch = any
os = any os = any
bits = 64 bits = 64
bigendian = no
prefetch = no prefetch = no
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -78,27 +83,6 @@ ifeq ($(ARCH),general-32)
arch = any arch = any
os = any os = any
bits = 32 bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-64)
arch = any
os = any
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-32)
arch = any
os = any
bits = 32
bigendian = yes
prefetch = no prefetch = no
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -109,7 +93,6 @@ ifeq ($(ARCH),x86-64)
arch = x86_64 arch = x86_64
os = any os = any
bits = 64 bits = 64
bigendian = no
prefetch = yes prefetch = yes
bsfq = yes bsfq = yes
popcnt = no popcnt = no
@@ -119,7 +102,6 @@ ifeq ($(ARCH),x86-64-modern)
arch = x86_64 arch = x86_64
os = any os = any
bits = 64 bits = 64
bigendian = no
prefetch = yes prefetch = yes
bsfq = yes bsfq = yes
popcnt = yes popcnt = yes
@@ -129,7 +111,6 @@ ifeq ($(ARCH),x86-32)
arch = i386 arch = i386
os = any os = any
bits = 32 bits = 32
bigendian = no
prefetch = yes prefetch = yes
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -139,7 +120,6 @@ ifeq ($(ARCH),x86-32-old)
arch = i386 arch = i386
os = any os = any
bits = 32 bits = 32
bigendian = no
prefetch = no prefetch = no
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -150,7 +130,6 @@ ifeq ($(ARCH),osx-ppc-64)
arch = ppc64 arch = ppc64
os = osx os = osx
bits = 64 bits = 64
bigendian = yes
prefetch = no prefetch = no
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -160,7 +139,6 @@ ifeq ($(ARCH),osx-ppc-32)
arch = ppc arch = ppc
os = osx os = osx
bits = 32 bits = 32
bigendian = yes
prefetch = no prefetch = no
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -170,7 +148,6 @@ ifeq ($(ARCH),osx-x86-64)
arch = x86_64 arch = x86_64
os = osx os = osx
bits = 64 bits = 64
bigendian = no
prefetch = yes prefetch = yes
bsfq = yes bsfq = yes
popcnt = no popcnt = no
@@ -180,7 +157,6 @@ ifeq ($(ARCH),osx-x86-32)
arch = i386 arch = i386
os = osx os = osx
bits = 32 bits = 32
bigendian = no
prefetch = yes prefetch = yes
bsfq = no bsfq = no
popcnt = no popcnt = no
@@ -223,6 +199,15 @@ ifeq ($(COMP),icc)
profile_clean = icc-profile-clean profile_clean = icc-profile-clean
endif endif
ifeq ($(COMP),clang)
comp=clang
CXX=clang++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
### 3.2 General compiler settings ### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS) CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
@@ -238,12 +223,24 @@ ifeq ($(comp),icc)
CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif endif
ifeq ($(comp),clang)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(os),osx) ifeq ($(os),osx)
CXXFLAGS += -arch $(arch) CXXFLAGS += -arch $(arch)
endif endif
### 3.3 General linker settings ### 3.3 General linker settings
LDFLAGS = -lpthread $(EXTRALDFLAGS) LDFLAGS = $(EXTRALDFLAGS)
### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw)
# Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
LDFLAGS += -lpthread
endif
endif
ifeq ($(os),osx) ifeq ($(os),osx)
LDFLAGS += -arch $(arch) LDFLAGS += -arch $(arch)
@@ -281,6 +278,20 @@ ifeq ($(optimize),yes)
CXXFLAGS += -O3 CXXFLAGS += -O3
endif endif
endif endif
ifeq ($(comp),clang)
### -O4 requires a linker that supports LLVM's LTO
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
endif endif
### 3.6. Bits ### 3.6. Bits
@@ -288,12 +299,7 @@ ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT CXXFLAGS += -DIS_64BIT
endif endif
### 3.7 Endianess ### 3.7 prefetch
ifeq ($(bigendian),yes)
CXXFLAGS += -DBIGENDIAN
endif
### 3.8 prefetch
ifeq ($(prefetch),yes) ifeq ($(prefetch),yes)
CXXFLAGS += -msse CXXFLAGS += -msse
DEPENDFLAGS += -msse DEPENDFLAGS += -msse
@@ -301,23 +307,34 @@ else
CXXFLAGS += -DNO_PREFETCH CXXFLAGS += -DNO_PREFETCH
endif endif
### 3.9 bsfq ### 3.8 bsfq
ifeq ($(bsfq),yes) ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ CXXFLAGS += -DUSE_BSFQ
endif endif
### 3.10 popcnt ### 3.9 popcnt
ifeq ($(popcnt),yes) ifeq ($(popcnt),yes)
CXXFLAGS += -DUSE_POPCNT CXXFLAGS += -msse3 -DUSE_POPCNT
endif
### 3.10 Link Time Optimization, it works since gcc 4.5 but not on mingw.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
ifeq ($(optimize),yes)
GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.`
GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
endif endif
### ========================================================================== ### ==========================================================================
### Section 4. Public targets ### Section 4. Public targets
### ========================================================================== ### ==========================================================================
default:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) build
help: help:
@echo "" @echo ""
@echo "To compile stockfish, type: " @echo "To compile stockfish, type: "
@@ -328,7 +345,6 @@ help:
@echo "" @echo ""
@echo "build > Build unoptimized version" @echo "build > Build unoptimized version"
@echo "profile-build > Build PGO-optimized version" @echo "profile-build > Build PGO-optimized version"
@echo "popcnt-profile-build > Build PGO-optimized version with optional popcnt-support"
@echo "strip > Strip executable" @echo "strip > Strip executable"
@echo "install > Install executable" @echo "install > Install executable"
@echo "clean > Clean up" @echo "clean > Clean up"
@@ -337,8 +353,8 @@ help:
@echo "Supported archs:" @echo "Supported archs:"
@echo "" @echo ""
@echo "x86-64 > x86 64-bit" @echo "x86-64 > x86 64-bit"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt-instruction" @echo "x86-64-modern > x86 64-bit with runtime support for popcnt instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support" @echo "x86-32 > x86 32-bit excluding old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware" @echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit" @echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit" @echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@@ -346,14 +362,13 @@ help:
@echo "osx-x86-32 > x86-Mac OS X 32 bit" @echo "osx-x86-32 > x86-Mac OS X 32 bit"
@echo "general-64 > unspecified 64-bit" @echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit" @echo "general-32 > unspecified 32-bit"
@echo "bigendian-64 > unspecified 64-bit with bigendian byte order"
@echo "bigendian-32 > unspecified 32-bit with bigendian byte order"
@echo "" @echo ""
@echo "Supported comps:" @echo "Supported comps:"
@echo "" @echo ""
@echo "gcc > Gnu compiler (default)" @echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler" @echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows" @echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@echo "" @echo ""
@echo "Non-standard targets:" @echo "Non-standard targets:"
@echo "" @echo ""
@@ -389,34 +404,6 @@ profile-build:
@echo "Step 4/4. Deleting profile data ..." @echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
popcnt-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
@echo ""
@echo "Step 0/6. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/6. Building executable for benchmark (popcnt disabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 6/6. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
@echo ""
strip: strip:
strip $(EXE) strip $(EXE)
@@ -431,6 +418,9 @@ clean:
testrun: testrun:
@$(PGOBENCH) @$(PGOBENCH)
default:
help
### ========================================================================== ### ==========================================================================
### Section 5. Private targets ### Section 5. Private targets
### ========================================================================== ### ==========================================================================
@@ -445,7 +435,6 @@ config-sanity:
@echo "arch: '$(arch)'" @echo "arch: '$(arch)'"
@echo "os: '$(os)'" @echo "os: '$(os)'"
@echo "bits: '$(bits)'" @echo "bits: '$(bits)'"
@echo "bigendian: '$(bigendian)'"
@echo "prefetch: '$(prefetch)'" @echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'" @echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'" @echo "popcnt: '$(popcnt)'"
@@ -463,11 +452,10 @@ config-sanity:
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" test "$(arch)" = "ppc64" || test "$(arch)" = "ppc"
@test "$(os)" = "any" || test "$(os)" = "osx" @test "$(os)" = "any" || test "$(os)" = "osx"
@test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(bigendian)" = "yes" || test "$(bigendian)" = "no"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no" @test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS) $(CXX) -o $@ $(OBJS) $(LDFLAGS)
@@ -484,6 +472,7 @@ gcc-profile-make:
gcc-profile-use: gcc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use' \ EXTRACXXFLAGS='-fprofile-use' \
EXTRALDFLAGS='-lgcov' \
all all
gcc-profile-clean: gcc-profile-clean:
@@ -518,7 +507,7 @@ icc-profile-clean:
hpux: hpux:
$(MAKE) \ $(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG -DNO_PREFETCH' \ CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \ CXXFLAGS="" \
LDFLAGS="" \ LDFLAGS="" \
all all
+67 -78
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,18 +19,22 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <istream>
#include <vector> #include <vector>
#include "misc.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
static const string Defaults[] = { static const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -43,111 +47,96 @@ static const string Defaults[] = {
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
""
}; };
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the /// of positions for a given limit each. There are five parameters; the
/// transposition table size, the number of search threads that should /// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default /// be used, the limit value spent for each position (optional, default is
/// is ply 12), an optional file name where to look for positions in fen /// depth 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type /// format (defaults are the positions defined above) and the type of the
/// of the limit value: depth (default), time in secs or number of nodes. /// limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt.
void benchmark(int argc, char* argv[]) { void benchmark(const Position& current, istream& is) {
vector<string> fenList; string token;
SearchLimits limits; Search::LimitsType limits;
int64_t totalNodes; vector<string> fens;
int time;
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128"; string ttSize = (is >> token) ? token : "128";
string threads = argc > 3 ? argv[3] : "1"; string threads = (is >> token) ? token : "1";
string valStr = argc > 4 ? argv[4] : "12"; string limit = (is >> token) ? token : "12";
string fenFile = argc > 5 ? argv[5] : "default"; string fenFile = (is >> token) ? token : "default";
string valType = argc > 6 ? argv[6] : "depth"; string limitType = (is >> token) ? token : "depth";
Options["Hash"].set_value(ttSize); Options["Hash"] = ttSize;
Options["Threads"].set_value(threads); Options["Threads"] = threads;
Options["OwnBook"].set_value("false"); TT.clear();
if (limitType == "time")
limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms
else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str());
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else else
limits.maxDepth = atoi(valStr.c_str()); limits.depth = atoi(limit.c_str());
// Do we need to load positions from a given FEN file ? if (fenFile == "default")
if (fenFile != "default") fens.assign(Defaults, Defaults + 16);
else if (fenFile == "current")
fens.push_back(current.to_fen());
else
{ {
string fen; string fen;
ifstream f(fenFile.c_str()); ifstream file(fenFile.c_str());
if (f.is_open()) if (!file.is_open())
{ {
fenList.clear(); cerr << "Unable to open file " << fenFile << endl;
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
while (getline(file, fen))
if (!fen.empty())
fens.push_back(fen);
file.close();
} }
// Ok, let's start the benchmark ! int64_t nodes = 0;
totalNodes = 0; Search::StateStackPtr st;
time = get_system_time(); Time::point elapsed = Time::now();
for (size_t i = 0; i < fenList.size(); i++) for (size_t i = 0; i < fens.size(); i++)
{ {
Move moves[] = { MOVE_NONE }; Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread());
Position pos(fenList[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (valType == "perft") if (limitType == "perft")
{ {
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY); size_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
totalNodes += cnt; cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
} }
else else
{ {
if (!think(pos, limits, moves)) Threads.start_searching(pos, limits, vector<Move>(), st);
break; Threads.wait_for_search_finished();
nodes += Search::RootPosition.nodes_searched();
totalNodes += pos.nodes_searched();
} }
} }
time = get_system_time() - time; elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero'
cerr << "\n===============================" cerr << "\n==========================="
<< "\nTotal time (ms) : " << time << "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << totalNodes << "\nNodes searched : " << nodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << endl; << "\nNodes/second : " << 1000 * nodes / elapsed << 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
} }
+143 -197
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,260 +25,206 @@
namespace { namespace {
enum Result { enum Result {
RESULT_UNKNOWN, INVALID = 0,
RESULT_INVALID, UNKNOWN = 1,
RESULT_WIN, DRAW = 2,
RESULT_LOSS, WIN = 4
RESULT_DRAW
}; };
struct KPKPosition { inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
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]; }
Square whiteKingSquare, blackKingSquare, pawnSquare; struct KPKPosition {
Color sideToMove;
Result classify_leaf(int idx);
Result classify(int idx, Result db[]);
private:
template<Color Us> Result classify(const Result db[]) const;
template<Color Us> Bitboard k_attacks() const {
return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq];
}
Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; }
void decode_index(int idx);
Square wksq, bksq, psq;
Color stm;
}; };
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 // 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; // stm * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32]; uint32_t KPKBitbase[IndexMax / 32];
Result classify_wtm(const KPKPosition& pos, const Result bb[]); int index(Square wksq, Square bksq, Square psq, Color stm);
Result classify_btm(const KPKPosition& pos, const Result bb[]);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
} }
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) { uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
int index = compute_index(wksq, bksq, wpsq, stm); int idx = index(wksq, bksq, wpsq, stm);
return KPKBitbase[idx / 32] & (1 << (idx & 31));
return KPKBitbase[index / 32] & (1 << (index & 31));
} }
void init_kpk_bitbase() { void Bitbases::init_kpk() {
Result bb[IndexMax]; Result db[IndexMax];
KPKPosition pos; KPKPosition pos;
bool repeat; int idx, bit, repeat = 1;
// Initialize table // Initialize table with known win / draw positions
for (int i = 0; i < IndexMax; i++) for (idx = 0; idx < IndexMax; idx++)
{ db[idx] = pos.classify_leaf(idx);
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;
}
// Iterate until all positions are classified (30 cycles needed) // Iterate until all positions are classified (30 cycles needed)
do { while (repeat)
repeat = false; for (repeat = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN)
for (int i = 0; i < IndexMax; i++) repeat = 1;
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);
// Map 32 position results into one KPKBitbase[] entry // Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++) for (idx = 0; idx < IndexMax / 32; idx++)
for (int j = 0; j < 32; j++) for (bit = 0; bit < 32; bit++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS) if (db[32 * idx + bit] == WIN)
KPKBitbase[i] |= (1 << j); KPKBitbase[idx] |= 1 << bit;
} }
namespace { namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range // A KPK bitbase index is an integer in [0, IndexMax] range
// //
// Information is mapped in this way // Information is mapped in this way
// //
// bit 0: side to move (WHITE or BLACK) // bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8) // 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 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1) // 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) { int index(Square w, Square b, Square p, Color c) {
assert(square_file(wpsq) <= FILE_D); assert(file_of(p) <= FILE_D);
int p = int(square_file(wpsq)) + 4 * int(square_rank(wpsq) - 1); return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15);
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
assert(r >= 0 && r < IndexMax);
return r;
} }
void KPKPosition::from_index(int index) { void KPKPosition::decode_index(int idx) {
int s = (index / 8192) % 24; stm = Color(idx & 1);
bksq = Square((idx >> 1) & 63);
sideToMove = Color(index % 2); wksq = Square((idx >> 7) & 63);
blackKingSquare = Square((index / 2) % 64); psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
whiteKingSquare = Square((index / 128) % 64);
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
} }
bool KPKPosition::is_legal() const { Result KPKPosition::classify_leaf(int idx) {
if ( whiteKingSquare == pawnSquare decode_index(idx);
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return false;
if (sideToMove == WHITE) // Check if two pieces are on the same square or if a king can be captured
{ if ( wksq == psq || wksq == bksq || bksq == psq
if ( bit_is_set(wk_attacks(), blackKingSquare) || (k_attacks<WHITE>() & bksq)
|| bit_is_set(pawn_attacks(), blackKingSquare)) || (stm == WHITE && (p_attacks() & bksq)))
return false; return INVALID;
}
else if (bit_is_set(bk_attacks(), whiteKingSquare))
return false;
return true; // The position is an immediate win if it is white to move and the white
// pawn can be promoted without getting captured.
if ( rank_of(psq) == RANK_7
&& stm == WHITE
&& wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1
||(k_attacks<WHITE>() & (psq + DELTA_N))))
return WIN;
// Check for known draw positions
//
// Case 1: Stalemate
if ( stm == BLACK
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | p_attacks())))
return DRAW;
// Case 2: King can capture undefended pawn
if ( stm == BLACK
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
return DRAW;
// Case 3: Black king in front of white pawn
if ( bksq == psq + DELTA_N
&& rank_of(psq) < RANK_7)
return DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( stm == WHITE
&& wksq == psq + DELTA_N
&& bksq == wksq + DELTA_N + DELTA_N
&& rank_of(psq) < RANK_5)
return DRAW;
// Case 5: Stalemate with rook pawn
if ( bksq == SQ_A8
&& file_of(psq) == FILE_A)
return DRAW;
// Case 6: White king trapped on the rook file
if ( file_of(wksq) == FILE_A
&& file_of(psq) == FILE_A
&& rank_of(wksq) > rank_of(psq)
&& bksq == wksq + 2)
return DRAW;
return UNKNOWN;
} }
bool KPKPosition::is_immediate_draw() const { template<Color Us>
Result KPKPosition::classify(const Result db[]) const {
if (sideToMove == BLACK) // White to Move: 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
Bitboard wka = wk_attacks(); // positions classified as RESULT_DRAW, the current position is classified
Bitboard bka = bk_attacks(); // RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN.
//
// Black to Move: If one move leads to a position classified as RESULT_DRAW,
// the result of the current position is RESULT_DRAW. If all moves lead to
// positions classified as RESULT_WIN, the position is classified RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
// Case 1: Stalemate Result r = INVALID;
if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB) Bitboard b = k_attacks<Us>();
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 {
// 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));
}
Result classify_wtm(const KPKPosition& pos, const Result bb[]) {
// If one move leads to a position classified as RESULT_LOSS, the result
// of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = pos.wk_attacks();
while (b) while (b)
{ {
s = pop_1st_bit(&b); r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
r = bb[compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK)]; : db[index(wksq, pop_lsb(&b), psq, WHITE)];
if (r == RESULT_LOSS) if (Us == WHITE && (r & WIN))
return RESULT_WIN; return WIN;
if (r == RESULT_UNKNOWN) if (Us == BLACK && (r & DRAW))
unknownFound = true; return DRAW;
} }
// Pawn moves if (Us == WHITE && rank_of(psq) < RANK_7)
if (square_rank(pos.pawnSquare) < RANK_7)
{ {
s = pos.pawnSquare + DELTA_N; Square s = psq + DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)]; r |= db[index(wksq, bksq, s, BLACK)]; // Single push
if (r == RESULT_LOSS) if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
return RESULT_WIN; r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push
if (r == RESULT_UNKNOWN) if (r & WIN)
unknownFound = true; return WIN;
// Double pawn push
if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{
s += DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
} }
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN;
} }
Result KPKPosition::classify(int idx, Result db[]) {
Result classify_btm(const KPKPosition& pos, const Result bb[]) { decode_index(idx);
return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(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.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = pos.bk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = bb[compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE)];
if (r == RESULT_DRAW)
return RESULT_DRAW;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS;
} }
} }
+257 -392
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,470 +17,335 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cstring>
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "misc.h"
#include "rkiss.h"
#if defined(IS_64BIT) CACHE_LINE_ALIGNMENT
const uint64_t BMult[64] = { Bitboard RMasks[64];
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL, Bitboard RMagics[64];
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL, Bitboard* RAttacks[64];
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL, unsigned RShifts[64];
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] = { Bitboard BMasks[64];
0x0A8002C000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL, Bitboard BMagics[64];
0x0380180080141000ULL, 0x1A00060008211044ULL, 0x410001000A0C0008ULL, Bitboard* BAttacks[64];
0x9500060004008100ULL, 0x0100024284A20700ULL, 0x0000802140008000ULL, unsigned BShifts[64];
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] = { Bitboard SquareBB[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 SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8]; Bitboard FileBB[8];
Bitboard RankBB[8]; Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8]; Bitboard AdjacentFilesBB[8];
Bitboard ThisAndNeighboringFilesBB[8]; Bitboard ThisAndAdjacentFilesBB[8];
Bitboard InFrontBB[2][8]; Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64]; Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64]; Bitboard DistanceRingsBB[64][8];
Bitboard ForwardBB[2][64];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64]; Bitboard AttackSpanMask[2][64];
Bitboard PseudoAttacks[6][64];
Bitboard BishopPseudoAttacks[64]; int SquareDistance[64][64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
namespace { namespace {
void init_masks(); // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
void init_step_attacks(); const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL;
void init_pseudo_attacks(); const uint32_t DeBruijn_32 = 0x783A9B23;
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
/// print_bitboard() prints a bitboard in an easily readable format to the int MS1BTable[256];
/// standard output. This is sometimes useful for debugging. Square BSFTable[64];
Bitboard RTable[0x19000]; // Storage space for rook attacks
Bitboard BTable[0x1480]; // Storage space for bishop attacks
uint8_t BitCount8Bit[256];
void print_bitboard(Bitboard b) { typedef unsigned (Fn)(Square, Bitboard);
for (Rank r = RANK_8; r >= RANK_1; r--) void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
{ Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
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 << "|\n"; FORCE_INLINE unsigned bsf_index(Bitboard b) {
if (Is64Bit)
return ((b & -b) * DeBruijn_64) >> 58;
// Use Matt Taylor's folding trick for 32 bit systems
b ^= (b - 1);
return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
} }
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
} }
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
/// first_1() finds the least significant nonzero bit in a nonzero bitboard. #if !defined(USE_BSFQ)
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if defined(IS_64BIT) && !defined(USE_BSFQ) Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
static CACHE_LINE_ALIGNMENT Square pop_lsb(Bitboard* b) {
const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26,
38, 15, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27,
33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61,
22, 43, 51, 60, 42, 59, 58
};
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b; Bitboard bb = *b;
*b &= (*b - 1); *b = bb & (bb - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]); return BSFTable[bsf_index(bb)];
} }
#elif !defined(USE_BSFQ) Square msb(Bitboard b) {
static CACHE_LINE_ALIGNMENT unsigned b32;
const int BitTable[64] = { int result = 0;
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) { if (b > 0xFFFFFFFF)
{
b >>= 32;
result = 32;
}
b ^= (b - 1); b32 = unsigned(b);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
}
// Use type-punning if (b32 > 0xFFFF)
union b_union { {
b32 >>= 16;
result += 16;
}
Bitboard b; if (b32 > 0xFF)
struct { {
#if defined (BIGENDIAN) b32 >>= 8;
uint32_t h; result += 8;
uint32_t l; }
#else
uint32_t l;
uint32_t h;
#endif
} dw;
};
Square pop_1st_bit(Bitboard* bb) { return (Square)(result + MS1BTable[b32]);
b_union u;
Square ret;
u.b = *bb;
if (u.dw.l)
{
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
u.dw.l &= (u.dw.l - 1);
*bb = u.b;
return ret;
}
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
u.dw.h &= (u.dw.h - 1);
*bb = u.b;
return ret;
} }
#endif // !defined(USE_BSFQ) #endif // !defined(USE_BSFQ)
/// init_bitboards() initializes various bitboard arrays. It is called during /// Bitboards::print() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void Bitboards::print(Bitboard b) {
sync_cout;
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File file = FILE_A; file <= FILE_H; file++)
std::cout << "| " << (b & (file | rank) ? "X " : " ");
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl;
}
/// Bitboards::init() initializes various bitboard arrays. It is called during
/// program initialization. /// program initialization.
void init_bitboards() { void Bitboards::init() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; for (int k = 0, i = 0; i < 8; i++)
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}}; while (k < (2 << i))
MS1BTable[k++] = i;
init_masks(); for (int i = 0; i < 64; i++)
init_step_attacks(); BSFTable[bsf_index(1ULL << i)] = Square(i);
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas); for (Bitboard b = 0; b < 256; b++)
init_pseudo_attacks(); BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
init_between_bitboards();
for (Square s = SQ_A1; s <= SQ_H8; s++)
SquareBB[s] = 1ULL << s;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int i = 1; i < 8; i++)
{
FileBB[i] = FileBB[i - 1] << 1;
RankBB[i] = RankBB[i - 1] << 8;
}
for (File f = FILE_A; f <= FILE_H; f++)
{
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (int d = 1; d < 8; d++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (int k = 0; steps[pt][k]; k++)
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (is_ok(to) && square_distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to;
}
Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (PseudoAttacks[QUEEN][s1] & s2)
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
BetweenBB[s1][s2] |= s;
}
} }
namespace { namespace {
// All functions below are used to precompute various bitboards during Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) {
// 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.
void init_masks() { Bitboard attack = 0;
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;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ for (Square s = sq + deltas[i];
dx = deltas[i][0]; is_ok(s) && square_distance(s, s - deltas[i]) == 1;
dy = deltas[i][1]; s += deltas[i])
f = fl + dx;
r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
{ {
attacks |= (1ULL << (f + r * 8)); attack |= s;
if (occupied & (1ULL << (f + r * 8))) if (occupied & s)
break; break;
f += dx;
r += dy;
} }
}
return attacks; return attack;
} }
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = EmptyBoardBB; Bitboard pick_random(RKISS& rk, int booster) {
int sq, cnt = 0;
while (mask) // Values s1 and s2 are used to rotate the candidate magic of a
{ // quantity known to be the optimal to quickly find the magics.
sq = pop_1st_bit(&mask); int s1 = booster & 63, s2 = (booster >> 6) & 63;
if (index & (1 << cnt++)) Bitboard m = rk.rand<Bitboard>();
result |= (1ULL << sq); m = (m >> s1) | (m << (64 - s1));
} m &= rk.rand<Bitboard>();
return result; m = (m >> s2) | (m << (64 - s2));
return m & rk.rand<Bitboard>();
} }
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++) // init_magics() computes all rook and bishop attacks at startup. Magic
{ // bitboards are used to look up attacks of sliding pieces. As a reference see
attackIndex[i] = index; // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
mask[i] = sliding_attacks(i, 0, deltas, 1, 6, 1, 6); // use the so called "fancy" approach.
j = 1 << ((CpuIs64Bit ? 64 : 32) - shift[i]);
for (int k = 0; k < j; k++) void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
{ Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
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;
}
}
void init_pseudo_attacks() { int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table;
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB); // Board edges are not considered in the relevant occupancies
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB); edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
// Given a square 's', the mask is the bitboard of sliding attacks from
// 's' computed on an empty board. The index must be big enough to contain
// all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attack bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attack(deltas, s, b);
b = (b - masks[s]) & masks[s];
} while (b);
// Set the offset for the table of the next square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size;
booster = MagicBoosters[Is64Bit][rank_of(s)];
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
do {
do magics[s] = pick_random(rk, booster);
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6);
memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side
// effect of verifying the magic.
for (i = 0; i < size; i++)
{
Bitboard& attack = attacks[s][index(s, occupancy[i])];
if (attack && attack != reference[i])
break;
assert(reference[i] != 0);
attack = reference[i];
}
} while (i != size);
} }
} }
void init_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);
}
}
} }
+140 -155
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,87 +23,75 @@
#include "types.h" #include "types.h"
const Bitboard EmptyBoardBB = 0; namespace Bitboards {
const Bitboard FileABB = 0x0101010101010101ULL; void init();
const Bitboard FileBBB = FileABB << 1; void print(Bitboard b);
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]; namespace Bitbases {
void init_kpk();
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
}
CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[64];
extern Bitboard RMagics[64];
extern Bitboard* RAttacks[64];
extern unsigned RShifts[64];
extern Bitboard BMasks[64];
extern Bitboard BMagics[64];
extern Bitboard* BAttacks[64];
extern unsigned BShifts[64];
extern Bitboard SquareBB[64];
extern Bitboard FileBB[8]; extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
extern Bitboard RankBB[8]; extern Bitboard RankBB[8];
extern Bitboard AdjacentFilesBB[8];
extern Bitboard ThisAndAdjacentFilesBB[8];
extern Bitboard InFrontBB[2][8]; extern Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttacksBB[16][64]; extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard DistanceRingsBB[64][8];
extern Bitboard SquaresInFrontMask[2][64]; extern Bitboard ForwardBB[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard AttackSpanMask[2][64];
extern Bitboard PseudoAttacks[6][64];
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
/// Functions for testing whether a given bit is set in a bitboard, and for /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard bit_is_set(Bitboard b, Square s) { inline Bitboard operator&(Bitboard b, Square s) {
return b & SetMaskBB[s]; return b & SquareBB[s];
} }
inline void set_bit(Bitboard *b, Square s) { inline Bitboard& operator|=(Bitboard& b, Square s) {
*b |= SetMaskBB[s]; return b |= SquareBB[s];
} }
inline void clear_bit(Bitboard *b, Square s) { inline Bitboard& operator^=(Bitboard& b, Square s) {
*b &= ClearMaskBB[s]; return b ^= SquareBB[s];
}
inline Bitboard operator|(Bitboard b, Square s) {
return b | SquareBB[s];
}
inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s];
} }
/// Functions used to update a bitboard after a move. This is faster /// more_than_one() returns true if in 'b' there is more than one bit set
/// then calling a sequence of clear_bit() + set_bit()
inline Bitboard make_move_bb(Square from, Square to) { inline bool more_than_one(Bitboard b) {
return SetMaskBB[from] | SetMaskBB[to]; return b & (b - 1);
}
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb;
} }
@@ -115,7 +103,7 @@ inline Bitboard rank_bb(Rank r) {
} }
inline Bitboard rank_bb(Square s) { inline Bitboard rank_bb(Square s) {
return RankBB[square_rank(s)]; return RankBB[rank_of(s)];
} }
inline Bitboard file_bb(File f) { inline Bitboard file_bb(File f) {
@@ -123,31 +111,23 @@ inline Bitboard file_bb(File f) {
} }
inline Bitboard file_bb(Square s) { 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 /// adjacent_files_bb takes a file as input and returns a bitboard representing
/// bitboard representing all squares on the neighboring files. /// all squares on the adjacent files.
inline Bitboard neighboring_files_bb(File f) { inline Bitboard adjacent_files_bb(File f) {
return NeighboringFilesBB[f]; return AdjacentFilesBB[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 /// this_and_adjacent_files_bb takes a file as input and returns a bitboard
/// a bitboard representing all squares on the given and neighboring files. /// representing all squares on the given and adjacent files.
inline Bitboard this_and_neighboring_files_bb(File f) { inline Bitboard this_and_adjacent_files_bb(File f) {
return ThisAndNeighboringFilesBB[f]; return ThisAndAdjacentFilesBB[f];
}
inline Bitboard this_and_neighboring_files_bb(Square s) {
return ThisAndNeighboringFilesBB[square_file(s)];
} }
@@ -162,72 +142,34 @@ inline Bitboard in_front_bb(Color c, Rank r) {
} }
inline Bitboard in_front_bb(Color c, Square s) { inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][square_rank(s)]; return InFrontBB[c][rank_of(s)];
} }
/// Functions for computing sliding attack bitboards. rook_attacks_bb(), /// between_bb returns a bitboard representing all squares between two squares.
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a /// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// bitboard of occupied squares as input, and return a bitboard representing /// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal,
/// all squares attacked by a rook, bishop or queen on the given square. /// 0 is returned.
#if defined(IS_64BIT) inline Bitboard between_bb(Square s1, Square s2) {
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
}
#else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
}
#endif
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
/// squares_between returns a bitboard representing all squares between
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
/// on the same line, file or diagonal, EmptyBoardBB is returned.
inline Bitboard squares_between(Square s1, Square s2) {
return BetweenBB[s1][s2]; return BetweenBB[s1][s2];
} }
/// squares_in_front_of takes a color and a square as input, and returns a /// forward_bb takes a color and a square as input, and returns a bitboard
/// bitboard representing all squares along the line in front of the square, /// representing all squares along the line in front of the square, from the
/// from the point of view of the given color. Definition of the table is: /// point of view of the given color. Definition of the table is:
/// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s) /// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
inline Bitboard squares_in_front_of(Color c, Square s) { inline Bitboard forward_bb(Color c, Square s) {
return SquaresInFrontMask[c][s]; return ForwardBB[c][s];
} }
/// passed_pawn_mask takes a color and a square as input, and returns a /// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is: /// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s) /// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_adjacent_files_bb(s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
@@ -237,7 +179,7 @@ inline Bitboard passed_pawn_mask(Color c, Square s) {
/// attack_span_mask takes a color and a square as input, and returns a bitboard /// attack_span_mask takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color /// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is: /// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s); /// AttackSpanMask[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) { inline Bitboard attack_span_mask(Color c, Square s) {
return AttackSpanMask[c][s]; return AttackSpanMask[c][s];
@@ -249,47 +191,90 @@ inline Bitboard attack_span_mask(Color c, Square s) {
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3)); & ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
} }
/// first_1() finds the least significant nonzero bit in a nonzero bitboard. /// same_color_squares() returns a bitboard representing all squares with
/// pop_1st_bit() finds and clears the least significant nonzero bit in a /// the same color of the given square.
/// nonzero bitboard.
inline Bitboard same_color_squares(Square s) {
return Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
}
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard
/// representing all squares attacked by Pt (bishop or rook) on the given square.
template<PieceType Pt>
FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) {
Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks;
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
if (Is64Bit)
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occ) & unsigned(Masks[s]);
unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occ) {
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
}
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if defined(USE_BSFQ) #if defined(USE_BSFQ)
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square first_1(Bitboard b) { FORCE_INLINE Square lsb(Bitboard b) {
unsigned long index; unsigned long index;
_BitScanForward64(&index, b); _BitScanForward64(&index, b);
return (Square) index; return (Square) index;
} }
#else
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen FORCE_INLINE Square msb(Bitboard b) {
Bitboard dummy; unsigned long index;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) ); _BitScanReverse64(&index, b);
return (Square) dummy; return (Square) index;
} }
#endif
FORCE_INLINE Square pop_1st_bit(Bitboard* b) { # else
const Square s = first_1(*b);
*b &= ~(1ULL<<s); FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard index;
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
FORCE_INLINE Square msb(Bitboard b) {
Bitboard index;
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
# endif
FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b);
*b &= ~(1ULL << s);
return s; return s;
} }
#else // if !defined(USE_BSFQ) #else // if !defined(USE_BSFQ)
extern Square first_1(Bitboard b); extern Square msb(Bitboard b);
extern Square pop_1st_bit(Bitboard* b); extern Square lsb(Bitboard b);
extern Square pop_lsb(Bitboard* b);
#endif #endif
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
#endif // !defined(BITBOARD_H_INCLUDED) #endif // !defined(BITBOARD_H_INCLUDED)
+33 -19
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,25 +21,29 @@
#if !defined(BITCOUNT_H_INCLUDED) #if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED #define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h" #include "types.h"
enum BitCountType { enum BitCountType {
CNT64, CNT_64,
CNT64_MAX15, CNT_64_MAX15,
CNT32, CNT_32,
CNT32_MAX15, CNT_32_MAX15,
CNT_POPCNT CNT_HW_POPCNT
}; };
/// count_1s() counts the number of nonzero bits in a bitboard. /// Determine at compile time the best popcount<> specialization according if
/// We have different optimized versions according if platform /// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// is 32 or 64 bits, and to the maximum number of nonzero bits. /// use hardware popcnt instruction when available.
/// We also support hardware popcnt instruction. See Readme.txt const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
/// on how to pgo compile with popcnt support. const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
template<BitCountType> inline int count_1s(Bitboard);
/// popcount() counts the number of nonzero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<> template<>
inline int count_1s<CNT64>(Bitboard b) { inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -48,7 +52,7 @@ inline int count_1s<CNT64>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) { inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL; b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; b *= 0x1111111111111111ULL;
@@ -56,7 +60,7 @@ inline int count_1s<CNT64_MAX15>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT32>(Bitboard b) { inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -69,7 +73,7 @@ inline int count_1s<CNT32>(Bitboard b) {
} }
template<> 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); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -81,17 +85,27 @@ inline int count_1s<CNT32_MAX15>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) { inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT) #if !defined(USE_POPCNT)
return int(b != 0); // Avoid 'b not used' warning
assert(false);
return b != 0; // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER) #elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b); return _mm_popcnt_u64(b);
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
return (int)__popcnt64(b); return (int)__popcnt64(b);
#elif defined(__GNUC__)
#else
unsigned long ret; unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b)); __asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret; return ret;
#endif #endif
} }
+130 -189
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,25 +17,36 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* /*
The code in this file is based on the opening book code in PolyGlot The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include "book.h" #include "book.h"
#include "misc.h"
#include "movegen.h" #include "movegen.h"
using namespace std; using namespace std;
namespace { namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with highest byte first (regardless of size).
// The entries are ordered according to the key in ascending order.
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
// Random numbers from PolyGlot, used to compute book hash keys // Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = { const Key PolyGlotRandoms[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -299,242 +310,172 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Indices to the Random64[] array // Offsets to the PolyGlotRandoms[] array of zobrist keys
const int PieceIdx = 0; const Key* ZobPiece = PolyGlotRandoms;
const int CastleIdx = 768; const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
const int EnPassantIdx = 772; const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
const int TurnIdx = 780; const Key* ZobTurn = ZobEnPassant + 8; // Number of files
// book_key() builds up a PolyGlot hash key out of a position // book_key() returns the PolyGlot hash key of the given position
uint64_t book_key(const Position& pos) { uint64_t book_key(const Position& pos) {
// Piece offset is calculated as (64 * PolyPieceType + square), where uint64_t key = 0;
// PolyPieceType is: BP = 0, WP = 1, BN = 2, WN = 3 .... BK = 10, WK = 11 Bitboard b = pos.pieces();
static const int PieceToPoly[] = { 0, 1, 3, 5, 7, 9, 11, 0, 0, 0, 2, 4, 6, 8, 10 };
uint64_t result = 0;
Bitboard b = pos.occupied_squares();
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); // In PolyGlotRandoms[] pieces are stored in the following sequence:
int p = PieceToPoly[int(pos.piece_on(s))]; // BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
result ^= Random64[PieceIdx + 64 * p + int(s)]; Square s = pop_lsb(&b);
Piece p = pos.piece_on(s);
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
key ^= ZobPiece[64 * pieceOfs + s];
} }
if (pos.can_castle_kingside(WHITE)) b = pos.can_castle(ALL_CASTLES);
result ^= Random64[CastleIdx + 0];
if (pos.can_castle_queenside(WHITE)) while (b)
result ^= Random64[CastleIdx + 1]; key ^= ZobCastle[pop_lsb(&b)];
if (pos.can_castle_kingside(BLACK))
result ^= Random64[CastleIdx + 2];
if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3];
if (pos.ep_square() != SQ_NONE) 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) if (pos.side_to_move() == WHITE)
result ^= Random64[TurnIdx]; key ^= ZobTurn[0];
return result; return key;
} }
} // namespace
PolyglotBook::PolyglotBook() {
for (int i = Time::now() % 10000; i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
/// converts them in a number of type T. A Polyglot book stores numbers in
/// big-endian format.
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
} }
/// Book c'tor. Make random number generation less deterministic, for book moves /// open() tries to open a book file with the given name after closing any
Book::Book() { /// exsisting one.
for (int i = abs(get_system_time() % 10000); i > 0; i--) bool PolyglotBook::open(const char* fName) {
RKiss.rand<unsigned>();
if (is_open()) // Cannot close an already closed file
close();
ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
return !fileName.empty();
} }
/// Book destructor. Be sure file is closed before we leave. /// 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.
Book::~Book() { Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
close(); if (fileName != fName && !open(fName.c_str()))
}
/// 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())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
}
// Set only if successful
bookName = fileName;
}
/// 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.
Move Book::get_move(const Position& pos, bool findBestMove) {
if (!bookFile.is_open() || bookSize == 0)
return MOVE_NONE; return MOVE_NONE;
BookEntry entry; BookEntry e;
int bookMove = MOVE_NONE; uint16_t best = 0;
unsigned score, scoresSum = 0, bestScore = 0; unsigned sum = 0;
Move move = MOVE_NONE;
uint64_t key = book_key(pos); uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position seekg(find_first(key) * sizeof(BookEntry), ios_base::beg);
for (int idx = find_entry(key); idx < bookSize; idx++)
while (*this >> e, e.key == key && good())
{ {
entry = read_entry(idx); best = max(best, e.count);
sum += e.count;
if (entry.key != key) // Choose book move according to its score. If a move has a very
break; // high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
score = entry.count; if ( (sum && RKiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
if (!findBestMove) move = Move(e.move);
{
// 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;
}
} }
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows: // A PolyGlot book move is encoded as follows:
// //
// bit 0- 5: destination square (from 0 to 63) // bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin 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 // Castling moves follow "king captures rook" representation. So in case book
// book move is a promotion we have to convert to our representation, in // move is a promotion we have to convert to our representation, in all the
// all other cases we can directly compare with a Move after having // other cases we can directly compare with a Move after having masked out
// masked out special Move's flags that are not supported by PolyGlot. // the special Move's flags (bit 14-15) that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7; int pt = (move >> 12) & 7;
if (pt)
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
if (p) // Add 'special move' flags and verify it is legal
bookMove = int(make_promotion_move(move_from(Move(bookMove)), for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
move_to(Move(bookMove)), PieceType(p + 1))); if (move == (ml.move() & 0x3FFF))
return ml.move();
// 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;
return MOVE_NONE; return MOVE_NONE;
} }
/// Book::find_entry() takes a book key as input, and does a binary search /// find_first() takes a book key as input, and does a binary search through
/// through the book file for the given key. The index to the first book /// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input is returned. When the key is not /// entry with the same key as the input.
/// found in the book file, bookSize is returned.
int Book::find_entry(uint64_t key) { size_t PolyglotBook::find_first(uint64_t key) {
int left, right, mid; seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
// 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::get_number() reads sizeof(T) chars from the file's binary byte
/// stream and converts them in a number of type T.
template<typename T>
void Book::get_number(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = (n << 8) + (T)bookFile.get();
}
/// 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());
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1;
BookEntry e; BookEntry e;
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg); assert(low <= high);
get_number(e.key); while (low < high && good())
get_number(e.move);
get_number(e.count);
get_number(e.learn);
if (!bookFile.good())
{ {
cerr << "Failed to read book entry at index " << idx << endl; mid = (low + high) / 2;
exit(EXIT_FAILURE);
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);
return low;
} }
+9 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,40 +23,23 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "move.h"
#include "position.h" #include "position.h"
#include "rkiss.h" #include "rkiss.h"
class PolyglotBook : private std::ifstream {
// 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;
uint16_t count;
uint32_t learn;
};
class Book {
public: public:
Book(); PolyglotBook();
~Book(); ~PolyglotBook();
void open(const std::string& fileName); Move probe(const Position& pos, const std::string& fName, bool pickBest);
void close();
Move get_move(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
private: private:
template<typename T> void get_number(T& n); template<typename T> PolyglotBook& operator>>(T& n);
BookEntry read_entry(int idx); bool open(const char* fName);
int find_entry(uint64_t key); size_t find_first(uint64_t key);
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS RKiss; RKISS RKiss;
std::string fileName;
}; };
#endif // !defined(BOOK_H_INCLUDED) #endif // !defined(BOOK_H_INCLUDED)
+282 -327
View File
File diff suppressed because it is too large Load Diff
+28 -20
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
#if !defined(ENDGAME_H_INCLUDED) #if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <string>
#include <map> #include <map>
#include <string>
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
@@ -46,6 +46,7 @@ enum EndgameType {
// Scaling functions // Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns KQKRPs, // KQ vs KR+pawns
@@ -60,25 +61,31 @@ enum EndgameType {
}; };
/// Endgame functions can be of two types according if return a Value or a
/// ScaleFactor. Type eg_fun<int>::type equals to either ScaleFactor or Value
/// depending if the template parameter is 0 or 1.
template<int> struct eg_fun { typedef Value type; };
template<> struct eg_fun<1> { typedef ScaleFactor type; };
/// Base and derived templates for endgame evaluation and scaling functions /// Base and derived templates for endgame evaluation and scaling functions
template<typename T> template<typename T>
struct EndgameBase { struct EndgameBase {
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {} virtual ~EndgameBase() {}
virtual Color color() const = 0; 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_fun<(E > SCALE_FUNS)>::type>
struct Endgame : public EndgameBase<T> { struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {} explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {}
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
T apply(const Position&) const; T operator()(const Position&) const;
private: private:
Color strongerSide, weakerSide; Color strongerSide, weakerSide;
@@ -87,26 +94,27 @@ private:
/// Endgames class stores in two std::map the pointers to endgame evaluation /// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual /// 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() that is virtual.
class Endgames { class Endgames {
typedef std::map<Key, EndgameBase<Value>* > EFMap; typedef std::map<Key, EndgameBase<eg_fun<0>::type>*> M1;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap; typedef std::map<Key, EndgameBase<eg_fun<1>::type>*> M2;
M1 m1;
M2 m2;
M1& map(M1::mapped_type) { return m1; }
M2& map(M2::mapped_type) { return m2; }
template<EndgameType E> void add(const std::string& code);
public: public:
Endgames(); Endgames();
~Endgames(); ~Endgames();
template<class T> T* get(Key key) const;
private: template<typename T> T probe(Key key, T& eg)
template<class T> void add(const std::string& keyCode); { return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
// 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;
}; };
#endif // !defined(ENDGAME_H_INCLUDED) #endif // !defined(ENDGAME_H_INCLUDED)
+933 -935
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,8 +24,14 @@
class Position; class Position;
namespace Eval {
extern Color RootColor;
extern void init();
extern Value evaluate(const Position& pos, Value& margin); extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace_evaluate(const Position& pos); extern std::string trace(const Position& pos);
extern void read_evaluation_uci_options(Color sideToMove);
}
#endif // !defined(EVALUATE_H_INCLUDED) #endif // !defined(EVALUATE_H_INCLUDED)
+6 -4
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,7 +20,9 @@
#if !defined(HISTORY_H_INCLUDED) #if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED #define HISTORY_H_INCLUDED
#include <algorithm>
#include <cstring> #include <cstring>
#include "types.h" #include "types.h"
/// The History class stores statistics about how often different moves /// The History class stores statistics about how often different moves
@@ -35,7 +37,7 @@ class History {
public: public:
void clear(); void clear();
Value value(Piece p, Square to) const; 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; Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g); void update_gain(Piece p, Square to, Value g);
@@ -55,7 +57,7 @@ inline Value History::value(Piece p, Square to) const {
return history[p][to]; 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; if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
} }
@@ -64,7 +66,7 @@ inline Value History::gain(Piece p, Square to) const {
} }
inline void History::update_gain(Piece p, Square to, Value g) { 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) #endif // !defined(HISTORY_H_INCLUDED)
-79
View File
@@ -1,79 +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(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
#if !defined(_MSC_VER)
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL)
# define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x)
# define cond_destroy(x) pthread_cond_destroy(x)
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# 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)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
# define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x)
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
#endif
#endif
#endif // !defined(LOCK_H_INCLUDED)
+15 -50
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,71 +17,36 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
#include <cstdio>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h" #include "evaluate.h"
#include "position.h" #include "position.h"
#include "thread.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
#ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void benchmark(int argc, char* argv[]);
extern void init_kpk_bitbase();
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Disable IO buffering for C and C++ standard libraries std::cout << engine_info() << std::endl;
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations UCI::init(Options);
init_bitboards(); Bitboards::init();
Position::init_zobrist(); Zobrist::init();
Position::init_piece_square_tables(); Bitbases::init_kpk();
init_kpk_bitbase(); Search::init();
init_search(); Eval::init();
Threads.init(); Threads.init();
TT.set_size(Options["Hash"]);
#ifdef USE_CALLGRIND std::string args;
CALLGRIND_START_INSTRUMENTATION;
#endif
if (argc < 2) for (int i = 1; i < argc; i++)
{ args += std::string(argv[i]) + " ";
// Print copyright notice
cout << engine_name() << " by " << engine_authors() << endl;
if (CpuHasPOPCNT) UCI::loop(args);
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)
benchmark(argc, argv);
else
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
Threads.exit(); Threads.exit();
return 0;
} }
+63 -72
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@@ -49,24 +50,24 @@ namespace {
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key. // 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<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
Endgame<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) }; Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) }; Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) }; Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) }; Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, KPKP>(BLACK) }; Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0 && pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame; && pos.non_pawn_material(Us) >= RookValueMg;
} }
template<Color Us> bool is_KBPsKs(const Position& pos) { template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1 && pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1; && pos.piece_count(Us, PAWN) >= 1;
} }
@@ -74,7 +75,7 @@ namespace {
template<Color Us> bool is_KQKRPs(const Position& pos) { template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0 return pos.piece_count(Us, PAWN) == 0
&& pos.non_pawn_material(Us) == QueenValueMidgame && pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1 && pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1 && pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1; && pos.piece_count(Them, PAWN) >= 1;
@@ -83,67 +84,57 @@ namespace {
} // namespace } // namespace
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames /// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry
/// object, and returns a pointer to it. If the material configuration is not
/// already present in the table, it is computed and stored there, so we don't
/// have to recompute everything when the same material configuration occurs again.
void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); } MaterialEntry* MaterialTable::probe(const Position& pos) {
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
Key key = pos.material_key();
MaterialEntry* e = entries[key];
/// MaterialInfoTable::get_material_info() takes a position object as input, // If e->key matches the position's material hash key, it means that we
/// 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 {
Key key = pos.get_material_key();
MaterialInfo* mi = probe(key);
// If mi->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply // have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it. // return the information we found the last time instead of recomputing it.
if (mi->key == key) if (e->key == key)
return mi; return e;
// Initialize MaterialInfo entry memset(e, 0, sizeof(MaterialEntry));
memset(mi, 0, sizeof(MaterialInfo)); e->key = key;
mi->key = key; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->gamePhase = MaterialTable::game_phase(pos);
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
// Let's look if we have a specialized evaluation function for this // Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed // particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed. // configuration one, then a generic one if previous search failed.
if ((mi->evaluationFunction = funcs->get<EndgameBase<Value> >(key)) != NULL) if (endgames.probe(key, e->evaluationFunction))
return mi; return e;
if (is_KXK<WHITE>(pos)) if (is_KXK<WHITE>(pos))
{ {
mi->evaluationFunction = &EvaluateKXK[WHITE]; e->evaluationFunction = &EvaluateKXK[WHITE];
return mi; return e;
} }
if (is_KXK<BLACK>(pos)) if (is_KXK<BLACK>(pos))
{ {
mi->evaluationFunction = &EvaluateKXK[BLACK]; e->evaluationFunction = &EvaluateKXK[BLACK];
return mi; return e;
} }
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{ {
// Minor piece endgame with at least one minor piece per side and // Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK. // no pawns. Note that the case KmmK is already handled by KXK.
assert((pos.pieces(KNIGHT, WHITE) | pos.pieces(BISHOP, WHITE))); assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(KNIGHT, BLACK) | pos.pieces(BISHOP, BLACK))); assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{ {
mi->evaluationFunction = &EvaluateKmmKm[WHITE]; e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return mi; return e;
} }
} }
@@ -154,26 +145,26 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
// scaling functions and we need to decide which one to use. // scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf; EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL) if (endgames.probe(key, sf))
{ {
mi->scalingFunction[sf->color()] = sf; e->scalingFunction[sf->color()] = sf;
return mi; return e;
} }
// Generic scaling functions that refer to more then one material // Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones. // distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function. // Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos)) if (is_KBPsKs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
if (is_KBPsKs<BLACK>(pos)) if (is_KBPsKs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
if (is_KQKRPs<WHITE>(pos)) if (is_KQKRPs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
else if (is_KQKRPs<BLACK>(pos)) else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
@@ -183,42 +174,42 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
if (pos.piece_count(BLACK, PAWN) == 0) if (pos.piece_count(BLACK, PAWN) == 0)
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (pos.piece_count(WHITE, PAWN) == 0)
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
} }
} }
// No pawns makes it difficult to win, even with a material advantage // No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame) if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg)
{ {
mi->factor[WHITE] = uint8_t e->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 < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
} }
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame) if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg)
{ {
mi->factor[BLACK] = uint8_t e->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 < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
} }
// Compute the space weight // Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame) if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{ {
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP) int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP); + pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount; e->spaceWeight = minorPieceCount * minorPieceCount;
} }
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
@@ -230,16 +221,16 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi; return e;
} }
/// MaterialInfoTable::imbalance() calculates imbalance comparing piece count of each /// MaterialTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors. /// piece type for both colors.
template<Color Us> template<Color Us>
int MaterialInfoTable::imbalance(const int pieceCount[][8]) { int MaterialTable::imbalance(const int pieceCount[][8]) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -253,7 +244,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
+ RedundantQueenPenalty * pieceCount[Us][QUEEN]; + RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad // 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]; pc = pieceCount[Us][pt1];
if (!pc) if (!pc)
@@ -261,7 +252,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
v = LinearCoefficients[pt1]; 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] v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
@@ -271,11 +262,11 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
} }
/// MaterialInfoTable::game_phase() calculates the phase given the current /// MaterialTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it /// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo. /// is stored in MaterialEntry.
Phase MaterialInfoTable::game_phase(const Position& pos) { Phase MaterialTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
+29 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,13 +21,20 @@
#define MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED
#include "endgame.h" #include "endgame.h"
#include "misc.h"
#include "position.h" #include "position.h"
#include "tt.h"
#include "types.h" #include "types.h"
const int MaterialTableSize = 8192; const int MaterialTableSize = 8192;
/// MaterialInfo is a class which contains various information about a /// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialEntry is a class which contains various information about a
/// material configuration. It contains a material balance evaluation, /// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in /// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will /// most cases is NULL, meaning that the standard evaluation function will
@@ -37,9 +44,9 @@ const int MaterialTableSize = 8192;
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor /// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn. /// of 4, which will result in scores of absolute value less than one pawn.
class MaterialInfo { class MaterialEntry {
friend class MaterialInfoTable; friend struct MaterialTable;
public: public:
Score material_value() const; Score material_value() const;
@@ -60,57 +67,53 @@ private:
}; };
/// The MaterialInfoTable class represents a pawn hash table. The most important /// The MaterialTable class represents a material hash table. The most important
/// method is get_material_info, which returns a pointer to a MaterialInfo object. /// method is probe(), which returns a pointer to a MaterialEntry object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> { struct MaterialTable {
public:
~MaterialInfoTable(); MaterialEntry* probe(const Position& pos);
void init();
MaterialInfo* get_material_info(const Position& pos) const;
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
template<Color Us> static int imbalance(const int pieceCount[][8]);
private: HashTable<MaterialEntry, MaterialTableSize> entries;
template<Color Us> Endgames endgames;
static int imbalance(const int pieceCount[][8]);
Endgames* funcs;
}; };
/// MaterialInfo::scale_factor takes a position and a color as input, and /// MaterialEntry::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the /// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not /// position in addition to the color, because the scale factor need not
/// to be a constant: It can also be a function which should be applied to /// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function /// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops. /// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const { inline ScaleFactor MaterialEntry::scale_factor(const Position& pos, Color c) const {
if (!scalingFunction[c]) if (!scalingFunction[c])
return ScaleFactor(factor[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; return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
} }
inline Value MaterialInfo::evaluate(const Position& pos) const { inline Value MaterialEntry::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos); return (*evaluationFunction)(pos);
} }
inline Score MaterialInfo::material_value() const { inline Score MaterialEntry::material_value() const {
return make_score(value, value); return make_score(value, value);
} }
inline int MaterialInfo::space_weight() const { inline int MaterialEntry::space_weight() const {
return spaceWeight; return spaceWeight;
} }
inline Phase MaterialInfo::game_phase() const { inline Phase MaterialEntry::game_phase() const {
return gamePhase; return gamePhase;
} }
inline bool MaterialInfo::specialized_eval_exists() const { inline bool MaterialEntry::specialized_eval_exists() const {
return evaluationFunction != NULL; return evaluationFunction != NULL;
} }
+139 -165
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,154 +17,180 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(_MSC_VER)
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
# if defined(__hpux)
# 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 <cassert>
#include <cstdio>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
using namespace std; using namespace std;
/// Version number. If EngineVersion is left empty, then AppTag plus /// Version number. If Version is left empty, then Tag plus current
/// current date (in the format YYMMDD) is used as a version number. /// date (in the format YYMMDD) is used as a version number.
static const string AppName = "Stockfish"; static const string Version = "2.3.1";
static const string EngineVersion = "2.1.1"; static const string Tag = "";
static const string AppTag = "";
/// 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 /// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending /// 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 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; string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
date >> month >> day >> year; s << "Stockfish " << Version;
s << setfill('0') << AppName + " " + AppTag + " " if (Version.empty())
<< year.substr(2, 2) << setw(2) {
<< (1 + months.find(month) / 4) << setw(2) date >> month >> day >> year;
<< day << cpu64;
s << Tag << setfill('0') << " " << year.substr(2)
<< setw(2) << (1 + months.find(month) / 4) << setw(2) << day;
}
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str(); return s.str();
} }
/// Our brave developers! Required by UCI /// Convert system time to milliseconds. That's all we need.
const string engine_authors() { Time::point Time::now() {
sys_time_t t; system_time(&t); return time_to_msec(t);
return "Tord Romstad, Marco Costalba and Joona Kiiski";
} }
/// Debug stuff. Helper functions used mainly for debugging purposes /// Debug functions used mainly to collect run-time statistics
static uint64_t dbg_hit_cnt0; static uint64_t hits[2], means[2];
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++;
}
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_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_before() { dbg_hit_on(false); } void dbg_mean_of(int v) { means[0]++; means[1] += v; }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
void dbg_print() {
/// get_system_time() returns the current system time, measured in milliseconds if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
int get_system_time() { if (means[0])
cerr << "Total " << means[0] << " Mean "
#if defined(_MSC_VER) << (float)means[1] / means[0] << endl;
struct _timeb t;
_ftime(&t);
return int(t.time * 1000 + t.millitm);
#else
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000 + t.tv_usec / 1000;
#endif
} }
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
/// can toggle the logging of std::cout and std:cin at runtime while preserving
/// usual i/o functionality and without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {}
int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); }
int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
int underflow() { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); }
streambuf* buf;
ofstream* file;
int log(int c, const char* prefix) {
static int last = '\n';
if (last == '\n')
file->rdbuf()->sputn(prefix, 3);
return last = file->rdbuf()->sputc((char)c);
}
};
class Logger {
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {}
~Logger() { start(false); }
ofstream file;
Tie in, out;
public:
static void start(bool b) {
static Logger l;
if (b && !l.file.is_open())
{
l.file.open("io_log.txt", ifstream::out | ifstream::app);
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
else if (!b && l.file.is_open())
{
cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf);
l.file.close();
}
}
};
/// Used to serialize access to std::cout to avoid multiple threads to write at
/// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == io_lock)
m.lock();
if (sc == io_unlock)
m.unlock();
return os;
}
/// Trampoline helper to avoid moving Logger to misc.h
void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores /// cpu_count() tries to detect the number of CPU cores
int cpu_count() { int cpu_count() {
#if defined(_MSC_VER) #if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s; SYSTEM_INFO s;
GetSystemInfo(&s); GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS); return s.dwNumberOfProcessors;
#else #else
# if defined(_SC_NPROCESSORS_ONLN) # if defined(_SC_NPROCESSORS_ONLN)
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS); return sysconf(_SC_NPROCESSORS_ONLN);
# elif defined(__hpux) # elif defined(__hpux)
struct pst_dynamic psd; struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1) if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1; return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS); return psd.psd_proc_cnt;
# else # else
return 1; return 1;
# endif # endif
@@ -173,78 +199,24 @@ 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 void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
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));
}
#if defined(_WIN32) || defined(_WIN64)
int tm = msec;
#else #else
timespec ts, *tm = &ts;
uint64_t ms = Time::now() + msec;
int input_available() { ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000LL;
static HANDLE inh = NULL;
static bool usePipe = false;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
if (!inh)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode(inh, &nchars))
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
}
// 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 #endif
cond_timedwait(sleepCond, sleepLock, tm);
}
/// prefetch() preloads the given address in L1/L2 cache. This is a non /// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be /// blocking function and do not stalls the CPU waiting for data to be
@@ -255,13 +227,15 @@ void prefetch(char*) {}
#else #else
# include <xmmintrin.h>
void prefetch(char* addr) { 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 // This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected. // Intel compiler. Both MSVC and gcc seems not affected.
__asm__ (""); __asm__ ("");
#endif # endif
_mm_prefetch(addr, _MM_HINT_T2); _mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead _mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
+37 -9
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,22 +20,50 @@
#if !defined(MISC_H_INCLUDED) #if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <fstream>
#include <string> #include <string>
#include <vector>
#include "types.h" #include "types.h"
extern const std::string engine_name(); extern const std::string engine_info(bool to_uci = false);
extern const std::string engine_authors();
extern int get_system_time();
extern int cpu_count(); extern int cpu_count();
extern int input_available(); extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr); extern void prefetch(char* addr);
extern void start_logger(bool b);
extern void dbg_hit_on(bool b); extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b); extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v); extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate(); extern void dbg_print();
extern void dbg_print_mean();
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(); }
};
namespace Time {
typedef int64_t point;
point now();
}
template<class Entry, int Size>
struct HashTable {
HashTable() : e(Size, Entry()) {}
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; }
private:
std::vector<Entry> e;
};
enum SyncCout { io_lock, io_unlock };
std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)
-258
View File
@@ -1,258 +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/>.
*/
#include <cassert>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "move.h"
#include "movegen.h"
#include "search.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.
const string move_to_uci(Move m, bool chess960) {
Square from = move_from(m);
Square to = move_to(m);
string promotion;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (move_is_short_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8";
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))));
return square_to_string(from) + square_to_string(to) + promotion;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
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;
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
/// 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";
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);
}
if (pos.move_is_capture(m))
{
if (pt == PAWN)
san += file_to_char(square_file(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
{
san += '=';
san += piece_type_to_char(move_promotion_piece(m));
}
}
// The move gives check? We don't use pos.move_gives_check() here
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_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();
}
}
-195
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)
+317 -414
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,524 +19,427 @@
#include <cassert> #include <cassert>
#include "bitcount.h"
#include "movegen.h" #include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility, /// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'. /// hardcoded names 'mlist' and 'from'.
#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b)) #define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b))
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(*mlist++).move = make_move(to - (d), to); }
namespace { namespace {
enum CastlingSide { template<CastlingSide Side, bool Checks, bool Chess960>
KING_SIDE, MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
QUEEN_SIDE
};
template<CastlingSide> if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us);
template<Color, MoveType>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN);
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];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
if ((from = *ptr++) == SQ_NONE)
return mlist; return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares(); // After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(us, Side);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us);
do assert(!pos.in_check());
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from)) const int K = Chess960 ? kto > kfrom ? -1 : 1
continue; : Side == KING_SIDE ? -1 : 1;
b = pos.attacks_from<Pt>(from) & checkSqs; for (Square s = kto; s != kfrom; s += (Square)K)
SERIALIZE_MOVES(b); if (pos.attackers_to(s) & enemies)
return mlist;
} while ((from = *ptr++) != SQ_NONE); // Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
return mlist;
(*mlist++).move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--;
return mlist; return mlist;
} }
template<>
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq) template<Square Delta>
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq)); inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8
: Delta == DELTA_S ? p >> 8
: Delta == DELTA_NE ? (p & ~FileHBB) << 9
: Delta == DELTA_SE ? (p & ~FileHBB) >> 7
: Delta == DELTA_NW ? (p & ~FileABB) << 7
: Delta == DELTA_SW ? (p & ~FileABB) >> 9 : 0;
} }
template<PieceType Pt, MoveType Type>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN); template<GenType Type, Square Delta>
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION); inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) {
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE) Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
}
template<PieceType Pt> while (b)
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Pt);
if (*ptr != SQ_NONE)
{ {
do { Square to = pop_lsb(&b);
from = *ptr;
b = pos.attacks_from<Pt>(from) & target; if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
SERIALIZE_MOVES(b); (*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN);
} while (*++ptr != SQ_NONE);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK);
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP);
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
}
// Knight-promotion is the only one that can give a direct check not
// already included in the queen-promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
} }
return mlist; return mlist;
} }
template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b; template<Color Us, GenType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist,
Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares;
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
Type == CAPTURES ? target : pos.pieces(Them));
// Single and double pawn pushes, no promotions
if (Type != CAPTURES)
{
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
{
b1 &= target;
b2 &= target;
}
if (Type == QUIET_CHECKS)
{
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
// Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovery check
// promotion has been already generated among captures.
if (pawnsNotOn7 & ci->dcCandidates)
{
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_PAWNS(b1, UP);
SERIALIZE_PAWNS(b2, UP + UP);
}
// Promotions and underpromotions
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
{
if (Type == CAPTURES)
emptySquares = ~pos.pieces();
if (Type == EVASIONS)
emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci);
}
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies;
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, RIGHT);
SERIALIZE_PAWNS(b2, LEFT);
if (pos.ep_square() != SQ_NONE)
{
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
// 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 == EVASIONS && !(target & (pos.ep_square() - UP)))
return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
}
}
return mlist;
}
template<PieceType Pt, bool Checks> FORCE_INLINE
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.piece_list(us, Pt);
for (Square from = *pl; from != SQ_NONE; from = *++pl)
{
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue;
if (ci->dcCandidates && (ci->dcCandidates & from))
continue;
}
Bitboard b = pos.attacks_from<Pt>(from) & target;
if (Checks)
b &= ci->checkSq[Pt];
SERIALIZE(b);
}
return mlist;
}
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
Color us, Bitboard target) {
Square from = pos.king_square(us); Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
b = pos.attacks_from<KING>(from) & target; SERIALIZE(b);
SERIALIZE_MOVES(b);
return mlist; return mlist;
} }
}
template<GenType Type> FORCE_INLINE
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci = NULL) {
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
mlist = generate_moves<KNIGHT, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<BISHOP, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<ROOK, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS)
mlist = generate_king_moves(pos, mlist, us, target);
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us))
{
if (pos.is_chess960())
{
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us);
}
else
{
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us);
}
}
return mlist;
}
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen } // namespace
/// generate<CAPTURES> generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list. /// promotions. Returns a pointer to the end of the move list.
/// ///
/// generate<MV_NON_CAPTURE> generates all pseudo-legal non-captures and /// generate<QUIETS> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list. /// underpromotions. Returns a pointer to the end of the move list.
/// ///
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and /// generate<NON_EVASIONS> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list. /// non-captures. Returns a pointer to the end of the move list.
template<MoveType Type> template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) { MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok()); assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.in_check()); assert(!pos.in_check());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Bitboard target; Bitboard target;
if (Type == MV_CAPTURE || Type == MV_NON_EVASION) if (Type == CAPTURES)
target = pos.pieces_of_color(opposite_color(us)); target = pos.pieces(~us);
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else
assert(false);
if (Type == MV_NON_EVASION) else if (Type == QUIETS)
{ target = ~pos.pieces();
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);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target); else if (Type == NON_EVASIONS)
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target); target = ~pos.pieces(us);
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) return generate_all_moves<Type>(pos, mlist, us, target);
{
if (pos.can_castle_kingside(us))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle_queenside(us))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
}
return mlist;
} }
// Explicit template instantiations // Explicit template instantiations
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist); template MoveStack* generate<CAPTURES>(const Position&, MoveStack*);
template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mlist); template MoveStack* generate<QUIETS>(const Position&, MoveStack*);
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist); template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) { MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check()); assert(!pos.in_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us)); CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
assert(pos.piece_on(ksq) == make_piece(opposite_color(us), KING)); while (dc)
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us);
while (b)
{ {
from = pop_1st_bit(&b); Square from = pop_lsb(&dc);
switch (pos.type_of_piece_on(from)) PieceType pt = type_of(pos.piece_on(from));
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break; if (pt == PAWN)
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break; continue; // Will be generated togheter with direct checks
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break; Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
default: assert(false); break; if (pt == KING)
} b &= ~PseudoAttacks[QUEEN][ci.ksq];
SERIALIZE(b);
} }
// Direct non-capture checks return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci);
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
} }
/// generate_evasions() generates all pseudo-legal check evasions when /// generate<EVASIONS> generates all pseudo-legal check evasions when the side
/// the side to move is in check. Returns a pointer to the end of the move list. /// to move is in check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) { MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.in_check()); assert(pos.in_check());
Bitboard b, target;
Square from, checksq; Square from, checksq;
int checkersCnt = 0; int checkersCnt = 0;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers(); Bitboard sliderAttacks = 0;
Bitboard sliderAttacks = EmptyBoardBB; Bitboard b = pos.checkers();
assert(pos.piece_on(ksq) == make_piece(us, KING)); assert(pos.checkers());
assert(checkers);
// Find squares attacked by slider checkers, we will remove // Find squares attacked by slider checkers, we will remove them from the king
// them from the king evasions set so to early skip known // evasions so to skip known illegal moves avoiding useless legality check later.
// illegal moves and avoid an useless legality check later.
b = checkers;
do do
{ {
checkersCnt++; checkersCnt++;
checksq = pop_1st_bit(&b); checksq = pop_lsb(&b);
assert(pos.color_of_piece_on(checksq) == opposite_color(us)); assert(color_of(pos.piece_on(checksq)) == ~us);
switch (pos.type_of_piece_on(checksq)) switch (type_of(pos.piece_on(checksq)))
{ {
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break; case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break; case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
case QUEEN: case QUEEN:
// In case of a queen remove also squares attacked in the other direction to // If queen and king are far or not on a diagonal line we can safely
// avoid possible illegal moves when queen and king are on adjacent squares. // remove all the squares attacked in the other direction becuase are
if (RookPseudoAttacks[checksq] & (1ULL << ksq)) // not reachable by the king anyway.
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq); if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq))
sliderAttacks |= PseudoAttacks[QUEEN][checksq];
// Otherwise 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 else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
default: default:
break; break;
} }
} while (b); } while (b);
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq; from = ksq;
SERIALIZE_MOVES(b); SERIALIZE(b);
// Generate evasions for other pieces only if not double check
if (checkersCnt > 1) if (checkersCnt > 1)
return mlist; return mlist; // Double check, only a king move can save the day
// Find squares where a blocking evasion or a capture of the // Generate blocking evasions or captures of the checking piece
// checker piece is possible. Bitboard target = between_bb(checksq, ksq) | pos.checkers();
target = squares_between(checksq, ksq) | checkers;
mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target); return generate_all_moves<EVASIONS>(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);
return generate_piece_moves<QUEEN>(pos, mlist, us, target);
} }
/// generate<MV_LEGAL / MV_PSEUDO_LEGAL> computes a complete list of legal /// generate<LEGAL> generates all the legal moves in the given position
/// 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);
}
template<> template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) { MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok()); MoveStack *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move());
MoveStack *last, *cur = mlist; end = pos.in_check() ? generate<EVASIONS>(pos, mlist)
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); : generate<NON_EVASIONS>(pos, mlist);
while (cur != end)
last = generate<MV_PSEUDO_LEGAL>(pos, mlist); if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
&& !pos.pl_move_is_legal(cur->move, pinned))
// Remove illegal moves from the list cur->move = (--end)->move;
while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
else else
cur++; cur++;
return last; return end;
} }
namespace {
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b = move_pawns<Delta>(pawns) & target & ~TFileABB;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
template<Color Us, 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);
Bitboard b;
Square to;
// Promotions and under-promotions, both captures and non-captures
b = move_pawns<Delta>(pawnsOn7) & target;
if (Delta != DELTA_N && Delta != DELTA_S)
b &= ~TFileABB;
while (b)
{
to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_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);
}
// 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);
else (void)pos; // Silence a warning under MSVC
}
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time, named
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard 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);
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));
// 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;
}
if (Type == MV_EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == MV_CAPTURE)
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);
}
// Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION)
{
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
// Consider only pawn moves which give direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures.
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;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
}
// En passant captures
if ((Type == MV_CAPTURE || Type == MV_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);
// 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))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB);
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_ep_move(to, pos.ep_square());
}
}
return mlist;
}
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);
assert(pos.piece_on(ksq) == make_piece(us, KING));
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.piece_on(rsq) == make_piece(us, ROOK));
// 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 (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
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);
return mlist;
}
} // namespace
+28 -13
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,21 +20,36 @@
#if !defined(MOVEGEN_H_INCLUDED) #if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include "move.h" #include "types.h"
#include "position.h"
enum MoveType { enum GenType {
MV_CAPTURE, CAPTURES,
MV_NON_CAPTURE, QUIETS,
MV_CHECK, QUIET_CHECKS,
MV_NON_CAPTURE_CHECK, EVASIONS,
MV_EVASION, NON_EVASIONS,
MV_NON_EVASION, LEGAL
MV_LEGAL,
MV_PSEUDO_LEGAL
}; };
template<MoveType> class Position;
template<GenType>
MoveStack* generate(const Position& pos, MoveStack* mlist); 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<GenType 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; }
size_t size() const { return last - mlist; }
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // !defined(MOVEGEN_H_INCLUDED)
+212 -198
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,174 +18,133 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "movegen.h" #include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "search.h" #include "thread.h"
#include "types.h"
namespace { namespace {
enum MovegenPhase { enum Sequencer {
PH_TT_MOVES, // Transposition table move and mate killer MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0 EVASION, EVASIONS_S2,
PH_KILLERS, // Killer moves from the current ply QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
PH_NONCAPTURES, // Non-captures and underpromotions QSEARCH_1, CAPTURES_S4,
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0 PROBCUT, CAPTURES_S5,
PH_EVASIONS, // Check evasions RECAPTURE, CAPTURES_S6,
PH_QCAPTURES, // Captures in quiescence search STOP
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
}; };
CACHE_LINE_ALIGNMENT // Unary predicate used by std::partition to split positive scores from remaining
const uint8_t MainSearchTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP }; // ones so to sort separately the two sets, and with the second sort delayed.
const uint8_t EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP }; inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP }; // Picks and moves to the front the best move in the range [begin, end),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end)
{
std::swap(*begin, *std::max_element(begin, end));
return begin;
}
} }
/// Constructor for the MovePicker class. Apart from the position for which /// Constructors of the MovePicker class. As arguments we pass information
/// it is asked to pick legal moves, MovePicker also wants some information
/// to help it to return the presumably good moves first, to decide which /// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to /// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
SearchStack* ss, Value beta) : pos(p), H(h) { Search::Stack* s, Value beta) : pos(p), H(h), depth(d) {
int searchTT = ttm;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move()); captureThreshold = 0;
cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1;
ss = s;
if (p.in_check()) if (p.in_check())
{ phase = EVASION;
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
else else
{ {
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller; phase = MAIN_SEARCH;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0]; killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low // Consider sligtly negative captures as good if at low depth and far from beta
// depth and far from beta. if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY)
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY) captureThreshold = -PawnValueMg;
badCaptureThreshold = -PawnValueMidgame;
phasePtr = MainSearchTable; // Consider negative captures as good if still enough to reach beta
else if (ss && ss->eval > beta)
captureThreshold = beta - ss->eval;
} }
phasePtr += int(!searchTT) - 1; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
go_next_phase(); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h) MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
: pos(p), H(h) { Square sq) : pos(p), H(h), cur(moves), end(moves) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check()) if (p.in_check())
phasePtr = EvasionTable; phase = EVASION;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable; else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES)
{
phase = QSEARCH_1;
// Skip TT move if is not a capture or a promotion, this avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases
// when TT table is full.
if (ttm && !pos.is_capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else else
{ {
phasePtr = QsearchWithoutChecksTable; phase = RECAPTURE;
recaptureSquare = sq;
// Skip TT move if is not a capture or a promotion, this avoids ttm = MOVE_NONE;
// 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;
} }
phasePtr += int(!searchTT) - 1; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
go_next_phase(); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), H(h), cur(moves), end(moves) {
/// MovePicker::go_next_phase() generates, scores and sorts the next bunch assert(!pos.in_check());
/// of moves when there are no more moves to try for the current phase.
void MovePicker::go_next_phase() { phase = PROBCUT;
curMove = moves; // In ProbCut we generate only captures better than parent's captured piece
phase = *(++phasePtr); captureThreshold = PieceValue[Mg][pt];
switch (phase) { ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
case PH_TT_MOVES: if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
curMove = ttMoves; ttMove = MOVE_NONE;
lastMove = curMove + 2;
return;
case PH_GOOD_CAPTURES: end += (ttMove != MOVE_NONE);
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_KILLERS:
curMove = killers;
lastMove = curMove + 2;
return;
case PH_NONCAPTURES:
lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture);
return;
case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just pick
// them in order to get SEE move ordering.
curMove = badCaptures;
lastMove = moves + MAX_MOVES;
return;
case PH_EVASIONS:
assert(pos.in_check());
lastMove = generate<MV_EVASION>(pos, moves);
score_evasions();
return;
case PH_QCAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
case PH_STOP:
lastMove = curMove + 1; // Avoid another go_next_phase() call
return;
default:
assert(false);
return;
}
} }
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and /// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score /// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be /// to each move in a move list. The moves with highest scores will be
/// picked first by get_next_move(). /// picked first by next_move().
void MovePicker::score_captures() { void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV/LVA.
@@ -203,148 +162,203 @@ void MovePicker::score_captures() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez). // some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m; Move m;
// Use MVV/LVA ordering for (MoveStack* it = moves; it != end; ++it)
for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = it->move;
if (move_is_promotion(m)) it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
cur->score = QueenValueMidgame; - type_of(pos.piece_moved(m));
else
cur->score = pos.midgame_value_of_piece_on(move_to(m)) if (type_of(m) == PROMOTION)
- pos.type_of_piece_on(move_from(m)); it->score += PieceValue[Mg][promotion_type(m)];
} }
} }
void MovePicker::score_noncaptures() { void MovePicker::score_noncaptures() {
Move m; Move m;
Square from;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* it = moves; it != end; ++it)
{ {
m = cur->move; m = it->move;
from = move_from(m); it->score = H.value(pos.piece_moved(m), to_sq(m));
cur->score = H.value(pos.piece_on(from), move_to(m));
} }
} }
void MovePicker::score_evasions() { void MovePicker::score_evasions() {
// Try good captures ordered by MVV/LVA, then non-captures if // Try good captures ordered by MVV/LVA, then non-captures if destination square
// destination square is not under attack, ordered by history // is not under attack, ordered by history value, then bad-captures and quiet
// value, and at the end bad-captures and non-captures with a // moves with a negative SEE. This last group is ordered by the SEE score.
// negative SEE. This last group is ordered by the SEE score.
Move m; Move m;
int seeScore; int seeScore;
// Skip if we don't have at least two moves to order if (end < moves + 2)
if (lastMove < moves + 2)
return; return;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* it = moves; it != end; ++it)
{ {
m = cur->move; m = it->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom it->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m)) else if (pos.is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m)) it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
- pos.type_of_piece_on(move_from(m)) + History::MaxValue; - type_of(pos.piece_moved(m)) + History::MaxValue;
else else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m)); it->score = H.value(pos.piece_moved(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::generate_next() generates, scores and sorts the next bunch of moves,
/// when there are no more moves to try for the current phase.
void MovePicker::generate_next() {
cur = moves;
switch (++phase) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves);
score_captures();
return;
case KILLERS_S1:
cur = killers;
end = cur + 2;
return;
case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves);
score_noncaptures();
end = std::partition(cur, end, has_positive_score);
sort<MoveStack>(cur, end);
return;
case QUIETS_2_S1:
cur = end;
end = endQuiets;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(cur, end);
return;
case BAD_CAPTURES_S1:
// Just pick them in reverse order to get MVV/LVA ordering
cur = moves + MAX_MOVES - 1;
end = endBadCaptures;
return;
case EVASIONS_S2:
end = generate<EVASIONS>(pos, moves);
score_evasions();
return;
case QUIET_CHECKS_S3:
end = generate<QUIET_CHECKS>(pos, moves);
return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP;
case STOP:
end = cur + 1; // Avoid another next_phase() call
return;
default:
assert(false);
}
}
/// MovePicker::next_move() is the most important method of the MovePicker class.
/// It returns a new pseudo legal move every time it is called, until there
/// are no more moves left. It picks the move with the biggest score from a list /// 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 /// 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 /// searched previously.
/// lock protected by caller when accessed through a shared MovePicker object. template<>
Move MovePicker::next_move<false>() {
Move MovePicker::get_next_move() {
Move move; Move move;
while (true) while (true)
{ {
while (curMove == lastMove) while (cur == end)
go_next_phase(); generate_next();
switch (phase) { switch (phase) {
case PH_TT_MOVES: case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
move = (curMove++)->move; cur++;
if ( move != MOVE_NONE return ttMove;
&& pos.move_is_legal(move, pinned))
return move;
break;
case PH_GOOD_CAPTURES: case CAPTURES_S1:
move = pick_best(curMove++, lastMove).move; move = pick_best(cur++, end)->move;
if ( move != ttMoves[0].move if (move != ttMove)
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{ {
// Check for a non negative SEE now assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold) if (pos.see_sign(move) >= captureThreshold)
return move; return move;
// Losing capture, move it to the tail of the array, note // Losing capture, move it to the tail of the array
// that move has now been already checked for legality. (endBadCaptures--)->move = move;
(--badCaptures)->move = move;
badCaptures->score = seeValue;
} }
break; break;
case PH_KILLERS: case KILLERS_S1:
move = (curMove++)->move; move = (cur++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned) && pos.is_pseudo_legal(move)
&& move != ttMoves[0].move && move != ttMove
&& move != ttMoves[1].move && !pos.is_capture(move))
&& !pos.move_is_capture(move))
return move; return move;
break; break;
case PH_NONCAPTURES: case QUIETS_1_S1: case QUIETS_2_S1:
// Sort negative scored moves only when we get there move = (cur++)->move;
if (curMove == lastGoodNonCapture) if ( move != ttMove
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move && move != killers[1].move)
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;
case PH_BAD_CAPTURES: case BAD_CAPTURES_S1:
move = pick_best(curMove++, lastMove).move; return (cur--)->move;
return move;
case PH_EVASIONS: case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
case PH_QCAPTURES: move = pick_best(cur++, end)->move;
move = pick_best(curMove++, lastMove).move; if (move != ttMove)
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;
case PH_QCHECKS: case CAPTURES_S5:
move = (curMove++)->move; move = pick_best(cur++, end)->move;
if ( move != ttMoves[0].move if (move != ttMove && pos.see(move) > captureThreshold)
&& pos.pl_move_is_legal(move, pinned)) return move;
break;
case CAPTURES_S6:
move = pick_best(cur++, end)->move;
if (to_sq(move) == recaptureSquare)
return move; return move;
break; break;
case PH_STOP: case QUIET_CHECKS_S3:
move = (cur++)->move;
if (move != ttMove)
return move;
break;
case STOP:
return MOVE_NONE; return MOVE_NONE;
default: default:
assert(false); assert(false);
break;
} }
} }
} }
/// Version of next_move() to use at split point nodes where the move is grabbed
/// from the split point's shared MovePicker object. This function is not thread
/// safe so should be lock protected by the caller.
template<>
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); }
+20 -19
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,42 +21,43 @@
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include "history.h" #include "history.h"
#include "move.h"
#include "position.h" #include "position.h"
#include "search.h"
#include "types.h" #include "types.h"
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from /// MovePicker class is used to pick one pseudo legal move at a time from the
/// the current position. It is initialized with a Position object and a few /// current position. The most important method is next_move(), which returns a
/// moves we have reason to believe are good. The most important method is /// new pseudo legal move each time it is called, until there are no moves left,
/// MovePicker::get_next_move(), which returns a new legal move each time it /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// is called, until there are no legal moves left, when MOVE_NONE is returned. /// beta algorithm, MovePicker attempts to return the moves which are most likely
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker /// to get a cut-off first.
/// attempts to return the moves which are most likely to get a cut-off first.
class MovePicker { class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value); MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&); MovePicker(const Position&, Move, Depth, const History&, Square);
Move get_next_move(); MovePicker(const Position&, Move, const History&, PieceType);
template<bool SpNode> Move next_move();
private: private:
void score_captures(); void score_captures();
void score_noncaptures(); void score_noncaptures();
void score_evasions(); void score_evasions();
void go_next_phase(); void generate_next();
const Position& pos; const Position& pos;
const History& H; const History& H;
Bitboard pinned; Search::Stack* ss;
MoveStack ttMoves[2], killers[2]; Depth depth;
int badCaptureThreshold, phase; Move ttMove;
const uint8_t* phasePtr; MoveStack killers[2];
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures; Square recaptureSquare;
int captureThreshold, phase;
MoveStack *cur, *end, *endQuiets, *endBadCaptures;
MoveStack moves[MAX_MOVES]; MoveStack moves[MAX_MOVES];
}; };
+271
View File
@@ -0,0 +1,271 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <iomanip>
#include <sstream>
#include <string>
#include "movegen.h"
#include "notation.h"
#include "position.h"
using namespace std;
static const char* PieceToChar = " PNBRQK pnbrqk";
/// score_to_uci() converts a value to a string suitable for use with the UCI
/// protocol specifications:
///
/// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
/// use negative values for y.
string score_to_uci(Value v, Value alpha, Value beta) {
stringstream s;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
s << "cp " << v * 100 / int(PawnValueMg);
else
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
return s.str();
}
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
/// mode. Internally castle moves are always coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (type_of(m) == CASTLE && !chess960)
to = (to > from ? FILE_G : FILE_C) | rank_of(from);
string move = square_to_string(from) + square_to_string(to);
if (type_of(m) == PROMOTION)
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case
return move;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent legal Move if any.
Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// move_to_san() takes a position and a legal Move as input and returns its
/// short algebraic notation representation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(pos.move_is_legal(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
string san;
Color us = pos.side_to_move();
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = pos.piece_on(from);
PieceType pt = type_of(pc);
if (type_of(m) == CASTLE)
san = to > from ? "O-O" : "O-O-O";
else
{
if (pt != PAWN)
{
san = PieceToChar[pt]; // Upper case
// Disambiguation if we have more then one piece with destination 'to'
// note that for pawns is not needed because starting file is explicit.
ambiguousMove = ambiguousFile = ambiguousRank = false;
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (attackers)
{
Square sq = pop_lsb(&attackers);
// Pinned pieces are not included in the possible sub-set
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
ambiguousFile |= file_of(sq) == file_of(from);
ambiguousRank |= rank_of(sq) == rank_of(from);
ambiguousMove = true;
}
if (ambiguousMove)
{
if (!ambiguousFile)
san += file_to_char(file_of(from));
else if (!ambiguousRank)
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
}
else if (pos.is_capture(m))
san = file_to_char(file_of(from));
if (pos.is_capture(m))
san += 'x';
san += square_to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[promotion_type(m)];
}
if (pos.move_gives_check(m, CheckInfo(pos)))
{
StateInfo st;
pos.do_move(m, st);
san += MoveList<LEGAL>(pos).size() ? "+" : "#";
pos.undo_move(m);
}
return san;
}
/// pretty_pv() formats human-readable search information, typically to be
/// appended to the search log file. It uses the two helpers below to pretty
/// format time and score respectively.
static string time_to_string(int64_t msecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int64_t hours = msecs / MSecHour;
int64_t minutes = (msecs % MSecHour) / MSecMinute;
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
stringstream s;
if (hours)
s << hours << ':';
s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return s.str();
}
static string score_to_string(Value v) {
stringstream s;
if (v >= VALUE_MATE_IN_MAX_PLY)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
return s.str();
}
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
StateInfo state[MAX_PLY_PLUS_2], *st = state;
Move* m = pv;
string san, padding;
size_t length;
stringstream s;
s << setw(2) << depth
<< setw(8) << score_to_string(value)
<< setw(8) << time_to_string(msecs);
if (pos.nodes_searched() < M)
s << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << setw(7) << pos.nodes_searched() / K << "K ";
else
s << setw(7) << pos.nodes_searched() / M << "M ";
padding = string(s.str().length(), ' ');
length = padding.length();
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
if (length + san.length() > 80)
{
s << "\n" + padding;
length = padding.length();
}
s << san << ' ';
length += san.length() + 1;
pos.do_move(*m++, *st++);
}
while (m != pv)
pos.undo_move(*--m);
return s.str();
}
+35
View File
@@ -0,0 +1,35 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(NOTATION_H_INCLUDED)
#define NOTATION_H_INCLUDED
#include <string>
#include "types.h"
class Position;
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
Move move_from_uci(const Position& pos, std::string& str);
const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED)
+116 -72
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
namespace { namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by opposed flag and file
@@ -63,62 +64,66 @@ namespace {
const Score PawnStructureWeight = S(233, 201); const Score PawnStructureWeight = S(233, 201);
#undef S // Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
const Value ShelterWeakness[2][8] =
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) },
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } };
inline Score apply_weight(Score v, Score w) { // Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100, const Value StormDanger[2][8] =
(int(eg_value(v)) * eg_value(w)) / 0x100); { { V(26), V(0), V(128), V(51), V(26) },
} { V(13), V(0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont.
const Value MaxSafetyBonus = V(263);
#undef S
#undef V
} }
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes /// PawnTable::probe() takes a position object as input, computes a PawnEntry
/// a PawnInfo object, and returns a pointer to it. The result is also stored /// object, and returns a pointer to it. The result is also stored in a hash
/// in an hash table, so we don't have to recompute everything when the same /// table, so we don't have to recompute everything when the same pawn structure
/// pawn structure occurs again. /// occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const { PawnEntry* PawnTable::probe(const Position& pos) {
assert(pos.is_ok()); Key key = pos.pawn_key();
PawnEntry* e = entries[key];
Key key = pos.get_pawn_key(); // If e->key matches the position's pawn hash key, it means that we
PawnInfo* pi = probe(key);
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return // have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it. // the information we found the last time instead of recomputing it.
if (pi->key == key) if (e->key == key)
return pi; return e;
// Initialize PawnInfo entry e->key = key;
pi->key = key; e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0;
pi->passedPawns[WHITE] = pi->passedPawns[BLACK] = 0; e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE; e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
// Calculate pawn attacks Bitboard wPawns = pos.pieces(WHITE, PAWN);
Bitboard wPawns = pos.pieces(PAWN, WHITE); Bitboard bPawns = pos.pieces(BLACK, PAWN);
Bitboard bPawns = pos.pieces(PAWN, BLACK); e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7);
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB); e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors and weight the result e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e)
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi) - evaluate_pawns<BLACK>(pos, bPawns, wPawns, e);
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
pi->value = apply_weight(pi->value, PawnStructureWeight); e->value = apply_weight(e->value, PawnStructureWeight);
return pi; return e;
} }
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color /// PawnTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us> template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) { Bitboard theirPawns, PawnEntry* e) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b; Bitboard b;
@@ -127,43 +132,43 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* 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 // 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)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s); f = file_of(s);
r = square_rank(s); r = rank_of(s);
// This file cannot be half open // This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f); e->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection // Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
// Flag the pawn as passed, isolated, doubled or member of a pawn // Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one). // chain (but not the backward one).
chain = ourPawns & adjacent_files_bb(f) & b;
isolated = !(ourPawns & adjacent_files_bb(f));
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
doubled = ourPawns & squares_in_front_of(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s);
isolated = !(ourPawns & neighboring_files_bb(f));
chain = ourPawns & neighboring_files_bb(f) & b;
// Test for backward pawn // Test for backward pawn
backward = false; backward = false;
// If the pawn is passed, isolated, or member of a pawn chain it cannot // If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on neighboring files // be backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either. // or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain) if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s)) && !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) && !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is // pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring // backward by looking in the forward direction on the adjacent
// files, and seeing whether we meet a friendly or an enemy pawn first. // files, and seeing whether we meet a friendly or an enemy pawn first.
b = pos.attacks_from<PAWN>(s, Us); b = pos.attacks_from<PAWN>(s, Us);
@@ -181,17 +186,17 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// A not passed pawn is a candidate to become passed if it is free to // A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this // advance and if the number of friendly pawns beside or behind this
// pawn on neighboring files is higher or equal than the number of // pawn on adjacent files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files. // enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated) candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB && (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns); && popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed // full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn. // pawn on each file is considered a true passed pawn.
if (passed && !doubled) if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s); e->passedPawns[Us] |= s;
// Score this pawn // Score this pawn
if (isolated) if (isolated)
@@ -209,35 +214,74 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidateBonus[relative_rank(Us, s)];
} }
return value; return value;
} }
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called /// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file
/// only when king square changes, about 20% of total king_shelter() calls. /// the king is on, as well as the two adjacent files.
template<Color Us> template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) { Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard pawns; Value safety = MaxSafetyBonus;
int r, shelter = 0; Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = file_of(ksq);
if (relative_rank(Us, ksq) <= RANK_4) kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf;
for (int f = kf - 1; f <= kf + 1; f++)
{ {
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq); // Shelter penalty is higher for the pawn in front of the king
r = ksq & (7 << 3); b = ourPawns & FileBB[f];
for (int i = 0; i < 3; i++) rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
{ safety -= ShelterWeakness[f != kf][rkUs];
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i); // Storm danger is smaller if enemy pawn is blocked
} b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
} }
return safety;
}
/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is
/// called only when king square changes, about 20% of total king_safety() calls.
template<Color Us>
Score PawnEntry::update_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0); castleRights[Us] = pos.can_castle(Us);
return kingShelters[Us]; minKPdistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castle if is bigger
if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]);
} }
// Explicit template instantiation // Explicit template instantiation
template Score PawnInfo::updateShelter<WHITE>(const Position& pos, Square ksq); template Score PawnEntry::update_safety<WHITE>(const Position& pos, Square ksq);
template Score PawnInfo::updateShelter<BLACK>(const Position& pos, Square ksq); template Score PawnEntry::update_safety<BLACK>(const Position& pos, Square ksq);
+34 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,22 +20,22 @@
#if !defined(PAWNS_H_INCLUDED) #if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED
#include "misc.h"
#include "position.h" #include "position.h"
#include "tt.h"
#include "types.h" #include "types.h"
const int PawnTableSize = 16384; const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn /// PawnEntry is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game /// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table /// to add further information in the future. A lookup to the pawn hash
/// (performed by calling the get_pawn_info method in a PawnInfoTable object) /// table (performed by calling the probe method in a PawnTable object)
/// returns a pointer to a PawnInfo object. /// returns a pointer to a PawnEntry object.
class PawnInfo { class PawnEntry {
friend class PawnInfoTable; friend struct PawnTable;
public: public:
Score pawns_value() const; Score pawns_value() const;
@@ -46,62 +46,70 @@ public:
int has_open_file_to_right(Color c, File f) const; int has_open_file_to_right(Color c, File f) const;
template<Color Us> template<Color Us>
Score king_shelter(const Position& pos, Square ksq); Score king_safety(const Position& pos, Square ksq);
private: private:
template<Color Us> template<Color Us>
Score updateShelter(const Position& pos, Square ksq); Score update_safety(const Position& pos, Square ksq);
template<Color Us>
Value shelter_storm(const Position& pos, Square ksq);
Key key; Key key;
Bitboard passedPawns[2]; Bitboard passedPawns[2];
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[2];
Square kingSquares[2]; Square kingSquares[2];
int minKPdistance[2];
int castleRights[2];
Score value; Score value;
int halfOpenFiles[2]; int halfOpenFiles[2];
Score kingShelters[2]; Score kingSafety[2];
}; };
/// The PawnInfoTable class represents a pawn hash table. The most important /// The PawnTable class represents a pawn hash table. The most important
/// method is get_pawn_info, which returns a pointer to a PawnInfo object. /// method is probe, which returns a pointer to a PawnEntry object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> { struct PawnTable {
public:
PawnInfo* get_pawn_info(const Position& pos) const; PawnEntry* probe(const Position& pos);
private:
template<Color Us> template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi); static Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e);
HashTable<PawnEntry, PawnTableSize> entries;
}; };
inline Score PawnInfo::pawns_value() const { inline Score PawnEntry::pawns_value() const {
return value; return value;
} }
inline Bitboard PawnInfo::pawn_attacks(Color c) const { inline Bitboard PawnEntry::pawn_attacks(Color c) const {
return pawnAttacks[c]; return pawnAttacks[c];
} }
inline Bitboard PawnInfo::passed_pawns(Color c) const { inline Bitboard PawnEntry::passed_pawns(Color c) const {
return passedPawns[c]; return passedPawns[c];
} }
inline int PawnInfo::file_is_half_open(Color c, File f) const { inline int PawnEntry::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f)); return halfOpenFiles[c] & (1 << int(f));
} }
inline int PawnInfo::has_open_file_to_left(Color c, File f) const { inline int PawnEntry::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1); return halfOpenFiles[c] & ((1 << int(f)) - 1);
} }
inline int PawnInfo::has_open_file_to_right(Color c, File f) const { inline int PawnEntry::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
} }
template<Color Us> template<Color Us>
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) { inline Score PawnEntry::king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq); return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<Us>(pos, ksq);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // !defined(PAWNS_H_INCLUDED)
+109
View File
@@ -0,0 +1,109 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PLATFORM_H_INCLUDED)
#define PLATFORM_H_INCLUDED
#if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4996) // Function _ftime() may be unsafe
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
# include <inttypes.h>
#endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
# include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
typedef pthread_t NativeHandle;
typedef void*(*pt_start_fn)(void*);
# define lock_init(x) pthread_mutex_init(&(x), NULL)
# define lock_grab(x) pthread_mutex_lock(&(x))
# define lock_release(x) pthread_mutex_unlock(&(x))
# define lock_destroy(x) pthread_mutex_destroy(&(x))
# define cond_destroy(x) pthread_cond_destroy(&(x))
# define cond_init(x) pthread_cond_init(&(x), NULL)
# define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW
# include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
#if !defined(NOMINMAX)
# define NOMINMAX // disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// We use critical sections on Windows to support Windows XP and older versions,
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle;
# define lock_init(x) InitializeCriticalSection(&(x))
# define lock_grab(x) EnterCriticalSection(&(x))
# define lock_release(x) LeaveCriticalSection(&(x))
# define lock_destroy(x) DeleteCriticalSection(&(x))
# define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(x)
# define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL)
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif
#endif // !defined(PLATFORM_H_INCLUDED)
+921 -1315
View File
File diff suppressed because it is too large Load Diff
+173 -294
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,44 +20,25 @@
#if !defined(POSITION_H_INCLUDED) #if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
#include <cassert>
#include "bitboard.h" #include "bitboard.h"
#include "move.h"
#include "types.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; class Position;
class Thread;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo { struct CheckInfo {
explicit CheckInfo(const Position&); explicit CheckInfo(const Position&);
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard pinned;
Square ksq; 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
}; };
@@ -68,17 +49,25 @@ enum Phase {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare;
Score value;
Value npMaterial[2]; Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score psqScore;
Square epSquare;
PieceType capturedType;
Key key; Key key;
Bitboard checkersBB; Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous; StateInfo* previous;
}; };
struct ReducedStateInfo {
Key pawnKey, materialKey;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score psqScore;
Square epSquare;
};
/// The position data structure. A position consists of the following data: /// The position data structure. A position consists of the following data:
/// ///
@@ -103,172 +92,112 @@ struct StateInfo {
/// * A counter for detecting 50 move rule draws. /// * A counter for detecting 50 move rule draws.
class Position { class Position {
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
public: public:
enum GamePhase { Position() {}
MidGame, Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
EndGame Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); }
}; Position& operator=(const Position&);
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
// Text input/output // Text input/output
void from_fen(const std::string& fen, bool isChess960); void from_fen(const std::string& fen, bool isChess960, Thread* th);
const std::string to_fen() const; const std::string to_fen() const;
void print(Move m = MOVE_NONE) const; void print(Move m = MOVE_NONE) const;
// Copying // Position representation
void flip(); Bitboard pieces() const;
// 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;
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const;
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(PieceType pt1, PieceType pt2, Color c) const; Bitboard pieces(Color c) const;
Bitboard pieces(Color c, PieceType pt) const;
// Number of pieces of each color and type Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
Piece piece_on(Square s) const;
Square king_square(Color c) const;
Square ep_square() const;
bool is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const;
int piece_count(Color c, PieceType pt) const; int piece_count(Color c, PieceType pt) const;
// The en passant square // Castling
Square ep_square() const; int can_castle(CastleRight f) const;
int can_castle(Color c) const;
bool castle_impeded(Color c, CastlingSide s) const;
Square castle_rook_square(Color c, CastlingSide s) const;
// Current king position for each color // Checking
Square king_square(Color c) const;
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
// Checking pieces and under check information
Bitboard checkers() const;
bool in_check() const; bool in_check() const;
Bitboard checkers() const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const;
// Piece lists // Attacks to/from a given square
Square piece_list(Color c, PieceType pt, int index) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
// Information about attacks to or from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const; Bitboard attacks_from(Piece p, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ); static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves // Properties of moves
bool 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_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 move_attacks_square(Move m, Square s) const;
bool move_is_legal(const Move m) const;
// Piece captured with previous moves 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 piece_moved(Move m) const;
PieceType captured_piece_type() const; PieceType captured_piece_type() const;
// Information about pawns // Piece specific
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const;
// Weak squares bool opposite_bishops() const;
bool square_is_weak(Square s, Color c) const; bool bishop_pair(Color c) const;
// Doing and undoing moves // Doing and undoing moves
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
void do_null_move(StateInfo& st); template<bool Do> void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
Key get_key() const; Key key() const;
Key get_exclusion_key() const; Key exclusion_key() const;
Key get_pawn_key() const; Key pawn_key() const;
Key get_material_key() const; Key material_key() const;
// Incremental evaluation // Incremental piece-square evaluation
Score value() const; Score psq_score() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
static Score pst_delta(Piece piece, Square from, Square to);
// Game termination checks
bool is_mate() const;
bool is_draw() const;
// Number of plies from starting position
int startpos_ply_counter() const;
// Other properties of the position // Other properties of the position
bool opposite_colored_bishops() const; Color side_to_move() const;
bool has_pawn_on_7th(Color c) const; int startpos_ply_counter() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const;
// Current thread ID searching on the position
int thread() const;
int64_t nodes_searched() const; int64_t nodes_searched() const;
void set_nodes_searched(int64_t n); void set_nodes_searched(int64_t n);
template<bool SkipRepetition> bool is_draw() const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const; bool pos_is_ok(int* failedStep = NULL) const;
void flip();
// Static member functions
static void init_zobrist();
static void init_piece_square_tables();
private: private:
// Initialization helpers (used while setting up a position)
// Initialization helper functions (used while setting up a position)
void clear(); void clear();
void detach();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void do_allow_oo(Color c); void set_castle_right(Color c, Square rfrom);
void do_allow_ooo(Color c);
bool set_castling_rights(char token);
// Helper functions for doing and undoing moves // Helper template functions
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep); template<bool Do> void do_castle_move(Move m);
void do_castle_move(Move m); template<bool FindPinned> Bitboard hidden_checkers() const;
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const; Key compute_key() const;
@@ -276,45 +205,28 @@ private:
Key compute_material_key() const; Key compute_material_key() const;
// Computing incremental evaluation scores and material counts // Computing incremental evaluation scores and material counts
static Score pst(Color c, PieceType pt, Square s); Score compute_psq_score() const;
Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
// Board // Board and pieces
Piece board[64]; Piece board[64]; // [square]
Bitboard byTypeBB[8]; // [pieceType]
// Bitboards Bitboard byColorBB[2]; // [color]
Bitboard byTypeBB[8], byColorBB[2]; int pieceCount[2][8]; // [color][pieceType]
Square pieceList[2][8][16]; // [color][pieceType][index]
// Piece counts int index[64]; // [square]
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Other info // Other info
Color sideToMove; int castleRightsMask[64]; // [square]
Key history[MaxGameLength]; Square castleRookSquare[2][2]; // [color][side]
int castleRightsMask[64]; Bitboard castlePath[2][2]; // [color][side]
StateInfo startState; StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes; int64_t nodes;
int startPosPly;
Color sideToMove;
Thread* thisThread;
StateInfo* st; StateInfo* st;
int chess960;
// Static variables
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
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 { inline int64_t Position::nodes_searched() const {
@@ -329,71 +241,47 @@ inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Color Position::color_of_piece_on(Square s) const { inline Piece Position::piece_moved(Move m) const {
return color_of_piece(piece_on(s)); return board[from_sq(m)];
} }
inline PieceType Position::type_of_piece_on(Square s) const { inline bool Position::is_empty(Square s) const {
return type_of_piece(piece_on(s)); return board[s] == NO_PIECE;
}
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)];
} }
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
return sideToMove; return sideToMove;
} }
inline Bitboard Position::occupied_squares() const { inline Bitboard Position::pieces() const {
return byTypeBB[0]; return byTypeBB[ALL_PIECES];
}
inline Bitboard Position::empty_squares() const {
return ~occupied_squares();
}
inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c];
} }
inline Bitboard Position::pieces(PieceType pt) const { inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt]; return byTypeBB[pt];
} }
inline Bitboard Position::pieces(PieceType pt, Color c) const {
return byTypeBB[pt] & byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return byTypeBB[pt1] | byTypeBB[pt2]; return byTypeBB[pt1] | byTypeBB[pt2];
} }
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const { inline Bitboard Position::pieces(Color c) const {
return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c]; return byColorBB[c];
}
inline Bitboard Position::pieces(Color c, PieceType pt) const {
return byColorBB[c] & byTypeBB[pt];
}
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
} }
inline int Position::piece_count(Color c, PieceType pt) const { inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt]; return pieceCount[c][pt];
} }
inline Square Position::piece_list(Color c, PieceType pt, int idx) const { inline const Square* Position::piece_list(Color c, PieceType pt) const {
return pieceList[c][pt][idx];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
return pieceList[c][pt]; return pieceList[c][pt];
} }
@@ -405,24 +293,28 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0]; return pieceList[c][KING][0];
} }
inline bool Position::can_castle_kingside(Color side) const { inline int Position::can_castle(CastleRight f) const {
return st->castleRights & (1+int(side)); return st->castleRights & f;
} }
inline bool Position::can_castle_queenside(Color side) const { inline int Position::can_castle(Color c) const {
return st->castleRights & (4+4*int(side)); return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
} }
inline bool Position::can_castle(Color side) const { inline bool Position::castle_impeded(Color c, CastlingSide s) const {
return can_castle_kingside(side) || can_castle_queenside(side); return byTypeBB[ALL_PIECES] & castlePath[c][s];
} }
inline Square Position::initial_kr_square(Color c) const { inline Square Position::castle_rook_square(Color c, CastlingSide s) const {
return relative_square(c, make_square(initialKRFile, RANK_1)); return castleRookSquare[c][s];
} }
inline Square Position::initial_qr_square(Color c) const { template<PieceType Pt>
return relative_square(c, make_square(initialQRFile, RANK_1)); inline Bitboard Position::attacks_from(Square s) const {
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, pieces())
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s];
} }
template<> template<>
@@ -430,24 +322,12 @@ inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s]; return StepAttacksBB[make_piece(c, PAWN)][s];
} }
template<PieceType Piece> // Knight and King and white pawns inline Bitboard Position::attacks_from(Piece p, Square s) const {
inline Bitboard Position::attacks_from(Square s) const { return attacks_from(p, s, byTypeBB[ALL_PIECES]);
return StepAttacksBB[Piece][s];
} }
template<> inline Bitboard Position::attackers_to(Square s) const {
inline Bitboard Position::attacks_from<BISHOP>(Square s) const { return attackers_to(s, byTypeBB[ALL_PIECES]);
return bishop_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<ROOK>(Square s) const {
return rook_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
} }
inline Bitboard Position::checkers() const { inline Bitboard Position::checkers() const {
@@ -455,100 +335,99 @@ inline Bitboard Position::checkers() const {
} }
inline bool Position::in_check() const { 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 { inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s)); return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
} }
inline bool Position::square_is_weak(Square s, Color c) const { inline Key Position::key() const {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
}
inline Key Position::get_key() const {
return st->key; return st->key;
} }
inline Key Position::get_exclusion_key() const { inline Key Position::exclusion_key() const {
return st->key ^ zobExclusion; return st->key ^ Zobrist::exclusion;
} }
inline Key Position::get_pawn_key() const { inline Key Position::pawn_key() const {
return st->pawnKey; return st->pawnKey;
} }
inline Key Position::get_material_key() const { inline Key Position::material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::pst(Color c, PieceType pt, Square s) { inline Score Position::psq_delta(Piece p, Square from, Square to) const {
return PieceSquareTable[make_piece(c, pt)][s]; return pieceSquareTable[p][to] - pieceSquareTable[p][from];
} }
inline Score Position::pst_delta(Piece piece, Square from, Square to) { inline Score Position::psq_score() const {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from]; return st->psqScore;
}
inline Score Position::value() const {
return st->value;
} }
inline Value Position::non_pawn_material(Color c) const { inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c]; return st->npMaterial[c];
} }
inline bool Position::move_is_passed_pawn_push(Move m) const { inline bool Position::is_passed_pawn_push(Move m) const {
Color c = side_to_move(); return type_of(piece_moved(m)) == PAWN
return piece_on(move_from(m)) == make_piece(c, PAWN) && pawn_is_passed(sideToMove, to_sq(m));
&& pawn_is_passed(c, move_to(m));
} }
inline int Position::startpos_ply_counter() const { inline int Position::startpos_ply_counter() const {
return startPosPlyCounter; return startPosPly + st->pliesFromNull; // HACK
} }
inline bool Position::opposite_colored_bishops() const { inline bool Position::opposite_bishops() const {
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1 return pieceCount[WHITE][BISHOP] == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0)); && pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
} }
inline bool Position::has_pawn_on_7th(Color c) const { inline bool Position::bishop_pair(Color c) const {
return pieces(PAWN, c) & rank_bb(relative_rank(c, RANK_7));
return pieceCount[c][BISHOP] >= 2
&& opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]);
}
inline bool Position::pawn_on_7th(Color c) const {
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
} }
inline bool Position::is_chess960() const { inline bool Position::is_chess960() const {
return chess960; 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 ! assert(is_ok(m));
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); return type_of(m) ? type_of(m) != CASTLE : !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 ! // Note that castle is coded as "king captures the rook"
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); assert(is_ok(m));
return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
} }
inline PieceType Position::captured_piece_type() const { inline PieceType Position::captured_piece_type() const {
return st->capturedType; return st->capturedType;
} }
inline int Position::thread() const { inline Thread* Position::this_thread() const {
return threadID; return thisThread;
}
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) #endif // !defined(POSITION_H_INCLUDED)
+61 -148
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,164 +22,77 @@
#include "types.h" #include "types.h"
namespace { #define S(mg, eg) make_score(mg, eg)
////
//// Constants modified by Joona Kiiski
////
const Value MP = PawnValueMidgame; /// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
const Value MK = KnightValueMidgame; /// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
const Value MB = BishopValueMidgame; /// for white side, for black side the tables are symmetric.
const Value MR = RookValueMidgame;
const Value MQ = QueenValueMidgame;
const Value EP = PawnValueEndgame; static const Score PSQT[][64] = {
const Value EK = KnightValueEndgame;
const Value EB = BishopValueEndgame;
const Value ER = RookValueEndgame;
const Value EQ = QueenValueEndgame;
const int MgPST[][64] = {
{ }, { },
{// Pawn { // Pawn
// A B C D E F G H S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
0, 0, 0, 0, 0, 0, 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),
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28, S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28, S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28, S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
0, 0, 0, 0, 0, 0, 0, 0
}, },
{// Knight { // Knight
// A B C D E F G H S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104),
MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135, S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79),
MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93, S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55),
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53, S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42),
MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25, S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42),
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11, S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55),
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11, S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79),
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53, S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104)
MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
}, },
{// Bishop { // Bishop
// A B C D E F G H S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59),
MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40, S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17, S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13, S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8, S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8, S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13, S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17, S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59)
MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
}, },
{// Rook { // Rook
// A B C D E F G H S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3)
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
}, },
{// Queen { // Queen
// A B C D E F G H S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80)
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
}, },
{// King { // King
//A B C D E F G H S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18),
287, 311, 262, 214, 214, 262, 311, 287, S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77),
262, 287, 238, 190, 190, 238, 287, 262, S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105),
214, 238, 190, 142, 142, 190, 238, 214, S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135),
190, 214, 167, 119, 119, 167, 214, 190, S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135),
167, 190, 142, 94, 94, 142, 190, 167, S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105),
142, 167, 119, 69, 69, 119, 167, 142, S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77),
119, 142, 94, 46, 46, 94, 142, 119, S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18)
94, 119, 69, 21, 21, 69, 119, 94
} }
}; };
const int EgPST[][64] = { #undef S
{ },
{// 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
#endif // !defined(PSQTAB_H_INCLUDED) #endif // !defined(PSQTAB_H_INCLUDED)
+15 -14
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,19 +20,6 @@
available under the GNU General Public License as published by available under the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (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) #if !defined(RKISS_H_INCLUDED)
@@ -40,6 +27,20 @@
#include "types.h" #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 { class RKISS {
// Keep variables always together // Keep variables always together
+946 -1363
View File
File diff suppressed because it is too large Load Diff
+64 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,53 +21,91 @@
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring> #include <cstring>
#include <memory>
#include <stack>
#include <vector>
#include "move.h" #include "misc.h"
#include "position.h"
#include "types.h" #include "types.h"
class Position;
struct SplitPoint; struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember namespace Search {
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
struct SearchStack { /// 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; int ply;
Move currentMove; Move currentMove;
Move mateKiller;
Move excludedMove; Move excludedMove;
Move bestMove;
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value eval; Value eval;
Value evalMargin; Value evalMargin;
bool skipNullMove; int skipNullMove;
SplitPoint* sp;
}; };
/// The SearchLimits struct stores information sent by GUI about available time /// RootMove struct is used for moves at the root of the tree. For each root
/// move we store a score, a node count, and a PV (really a refutation in the
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
/// all non-pv moves.
struct RootMove {
RootMove(){} // Needed by sort()
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
pv.push_back(m); pv.push_back(MOVE_NONE);
}
bool operator<(const RootMove& m) const { return score < m.score; }
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Value score;
Value prevScore;
std::vector<Move> pv;
};
/// The LimitsType struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode /// 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. /// 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 !(movetime | depth | nodes | infinite); }
SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon) int time[2], inc[2], movestogo, depth, nodes, movetime, infinite, ponder;
: 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;
}; };
extern void init_search();
extern int64_t perft(Position& pos, Depth depth); /// The SignalsType struct stores volatile flags updated during the search
extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]); /// typically in an async fashion, for instance to stop the search by the GUI.
struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
};
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<RootMove> RootMoves;
extern Position RootPosition;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
extern void init();
extern size_t perft(Position& pos, Depth depth);
extern void think();
} // namespace Search
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)
+322 -231
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,211 +17,256 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <iostream> #include <iostream>
#include "movegen.h"
#include "search.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
ThreadsManager Threads; // Global object definition using namespace Search;
ThreadPool Threads; // Global object
namespace { extern "C" { namespace { extern "C" {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID. // is launched. It is a wrapper to member function pointed by start_fn.
// There are two versions of this function; one for POSIX threads and
// one for Windows threads.
#if defined(_MSC_VER) long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; }
DWORD WINAPI start_routine(LPVOID threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return 0;
}
#else
void* start_routine(void* threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
#endif
} } } }
// wake_up() wakes up the thread, normally at the beginning of the search or, // Thread c'tor starts a newly-created thread of execution that will call
// if "sleeping threads" is used, when there is some work to do. // the idle loop function pointed by start_fn going immediately to sleep.
void Thread::wake_up() { Thread::Thread(Fn fn) {
lock_grab(&sleepLock); is_searching = do_exit = false;
cond_signal(&sleepCond); maxPly = splitPointsCnt = 0;
lock_release(&sleepLock); curSplitPoint = NULL;
start_fn = fn;
idx = Threads.size();
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
} }
// cutoff_occurred() checks whether a beta cutoff has occurred in // Thread d'tor waits for thread termination before to return.
// the thread's currently active split point, or in some ancestor of
// the current split point. Thread::~Thread() {
assert(do_sleep);
do_exit = true; // Search must be already finished
wake_up();
thread_join(handle); // Wait for thread termination
}
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls check_time(). If maxPly is 0 thread sleeps until is woken up.
extern void check_time();
void Thread::timer_loop() {
while (!do_exit)
{
mutex.lock();
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
mutex.unlock();
check_time();
}
}
// Thread::main_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() {
while (true)
{
mutex.lock();
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_exit)
{
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
sleepCondition.wait(mutex);
}
mutex.unlock();
if (do_exit)
return;
is_searching = true;
Search::think();
assert(is_searching);
}
}
// Thread::wake_up() wakes up the thread, normally at the beginning of the search
// or, if "sleeping threads" is used at split time.
void Thread::wake_up() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::wait_for_stop_or_ponderhit() is called when the maximum depth is
// reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void Thread::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
mutex.lock();
while (!Signals.stop) sleepCondition.wait(mutex);;
mutex.unlock();
}
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const { bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent) for (SplitPoint* sp = curSplitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff) if (sp->cutoff)
return true; return true;
return false; return false;
} }
// is_available_to() checks whether the thread is available to help the thread with // Thread::is_available_to() checks whether the thread is available to help the
// threadID "master" at a split point. An obvious requirement is that thread must be // thread 'master' at a split point. An obvious requirement is that thread must
// idle. With more than two threads, this is not by itself sufficient: If the thread // be idle. With more than two threads, this is not sufficient: If the thread is
// is the master of some active split point, it is only available as a slave to the // the master of some active split point, it is only available as a slave to the
// threads which are busy searching the split point at the top of "slave"'s split // slaves which are busy searching the split point at the top of slaves split
// point stack (the "helpful master concept" in YBWC terminology). // point stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(int master) const { bool Thread::is_available_to(Thread* master) const {
if (state != AVAILABLE) if (is_searching)
return false; return false;
// Make a local copy to be sure doesn't become zero under our feet while // Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access. // testing next condition and so leading to an out of bound access.
int localActiveSplitPoints = activeSplitPoints; int spCnt = splitPointsCnt;
// No active split points means that the thread is available as a slave for any // No active split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible. // other thread otherwise apply the "helpful master" concept if possible.
if ( !localActiveSplitPoints return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx));
|| splitPoints[localActiveSplitPoints - 1].is_slave[master])
return true;
return false;
} }
// read_uci_options() updates number of active threads and other internal // init() is called at startup. Initializes lock and condition variable and
// parameters according to the UCI options values. It is called before // launches requested threads sending them immediately to sleep. We cannot use
// to start a new search. // a c'tor becuase Threads is a static object and we need a fully initialized
// engine at this point due to allocation of endgames in Thread c'tor.
void ThreadsManager::read_uci_options() { void ThreadPool::init() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>(); timer = new Thread(&Thread::timer_loop);
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY; threads.push_back(new Thread(&Thread::main_loop));
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>(); read_uci_options();
activeThreads = Options["Threads"].value<int>();
} }
// init() is called during startup. Initializes locks and condition variables // exit() cleanly terminates the threads before the program exits.
// and launches all threads sending them immediately to sleep.
void ThreadsManager::init() { void ThreadPool::exit() {
int threadID[MAX_THREADS]; for (size_t i = 0; i < threads.size(); i++)
delete threads[i];
// This flag is needed to properly end the threads when program exits delete timer;
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 // read_uci_options() updates internal threads parameters from the corresponding
init_hash_tables(); // UCI options and creates/destroys threads to match the requested number. Thread
// objects are dynamically allocated to avoid creating in advance all possible
// threads, with included pawns and material tables, if only few are used.
lock_init(&mpLock); void ThreadPool::read_uci_options() {
// Initialize thread and split point locks maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
for (int i = 0; i < MAX_THREADS; i++) minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
size_t requested = Options["Threads"];
assert(requested > 0);
while (threads.size() < requested)
threads.push_back(new Thread(&Thread::idle_loop));
while (threads.size() > requested)
{ {
lock_init(&threads[i].sleepLock); delete threads.back();
cond_init(&threads[i].sleepCond); threads.pop_back();
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
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++)
{
threads[i].state = Thread::INITIALIZING;
threadID[i] = i;
#if defined(_MSC_VER)
bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL);
#else
pthread_t pthreadID;
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
#endif
if (!ok)
{
std::cout << "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 // wake_up() is called before a new search to start the threads that are waiting
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
// threads will be woken up at split time.
void ThreadsManager::exit() { void ThreadPool::wake_up() const {
// Force the woken up threads to exit idle_loop() and hence terminate for (size_t i = 0; i < threads.size(); i++)
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
{ {
// Wake up all the threads and waits for termination threads[i]->maxPly = 0;
if (i != 0) threads[i]->do_sleep = false;
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
// Now we can safely destroy the locks and wait conditions if (!useSleepingThreads)
lock_destroy(&threads[i].sleepLock); threads[i]->wake_up();
cond_destroy(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
} }
lock_destroy(&mpLock);
} }
// init_hash_tables() dynamically allocates pawn and material hash tables // sleep() is called after the search finishes to ask all the threads but the
// according to the number of active threads. This avoids preallocating // main one to go waiting on a sleep condition.
// 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() { void ThreadPool::sleep() const {
for (int i = 0; i < activeThreads; i++) // Main thread will go to sleep by itself to avoid a race with start_searching()
{ for (size_t i = 1; i < threads.size(); i++)
threads[i].pawnTable.init(); threads[i]->do_sleep = true;
threads[i].materialTable.init();
}
} }
// available_slave_exists() tries to find an idle thread which is available as // 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 'master'.
bool ThreadsManager::available_slave_exists(int master) const { bool ThreadPool::available_slave_exists(Thread* master) const {
assert(master >= 0 && master < activeThreads); for (size_t i = 0; i < threads.size(); i++)
if (threads[i]->is_available_to(master))
for (int i = 0; i < activeThreads; i++)
if (i != master && threads[i].is_available_to(master))
return true; return true;
return false; return false;
@@ -229,117 +274,163 @@ bool ThreadsManager::available_slave_exists(int master) const {
// split() does the actual work of distributing the work at a node between // split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the // several available threads. If it does not succeed in splitting the node
// node (because no idle threads are available, or because we have no unused // (because no idle threads are available, or because we have no unused split
// split point objects), the function immediately returns. If splitting is // point objects), the function immediately returns. If splitting is possible, a
// possible, a SplitPoint object is initialized with all the data that must be // SplitPoint object is initialized with all the data that must be copied to the
// copied to the helper threads and we tell our helper threads that they have // helper threads and then helper threads are told that they have been assigned
// been assigned work. This will cause them to instantly leave their idle loops and // work. This will cause them to instantly leave their idle loops and call
// call search().When all threads have returned from search() then split() returns. // search(). When all threads have returned from search() then split() returns.
template <bool Fake> template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value* bestValue, Depth depth, Move threatMove, Value bestValue, Move* bestMove, Depth depth,
int moveCount, MovePicker* mp, bool pvNode) { Move threatMove, int moveCount, MovePicker* mp, int nodeType) {
assert(pos.is_ok());
assert(*bestValue >= -VALUE_INFINITE); assert(pos.pos_is_ok());
assert(*bestValue <= *alpha); assert(bestValue > -VALUE_INFINITE);
assert(*alpha < beta); assert(bestValue <= alpha);
assert(alpha < beta);
assert(beta <= VALUE_INFINITE); assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO); assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
assert(activeThreads > 1);
int i, master = pos.thread(); Thread* master = pos.this_thread();
Thread& masterThread = threads[master];
lock_grab(&mpLock); if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
return bestValue;
// If no other thread is available to help us, or if we have too many // Pick the next available split point from the split point stack
// active split points, don't split. SplitPoint& sp = master->splitPoints[master->splitPointsCnt];
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS) sp.parent = master->curSplitPoint;
sp.master = master;
sp.cutoff = false;
sp.slavesMask = 1ULL << master->idx;
sp.depth = depth;
sp.bestMove = *bestMove;
sp.threatMove = threatMove;
sp.alpha = alpha;
sp.beta = beta;
sp.nodeType = nodeType;
sp.bestValue = bestValue;
sp.mp = mp;
sp.moveCount = moveCount;
sp.pos = &pos;
sp.nodes = 0;
sp.ss = ss;
assert(master->is_searching);
master->curSplitPoint = &sp;
int slavesCnt = 0;
// Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
sp.mutex.lock();
mutex.lock();
for (size_t i = 0; i < threads.size() && !Fake; ++i)
if (threads[i]->is_available_to(master))
{
sp.slavesMask |= 1ULL << i;
threads[i]->curSplitPoint = &sp;
threads[i]->is_searching = true; // Slave leaves idle_loop()
if (useSleepingThreads)
threads[i]->wake_up();
if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
break;
}
master->splitPointsCnt++;
mutex.unlock();
sp.mutex.unlock();
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set.
// The thread will return from the idle loop when all slaves have finished
// their work at this split point.
if (slavesCnt || Fake)
{ {
lock_release(&mpLock); master->idle_loop();
return;
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!master->is_searching);
} }
// Pick the next available split point object from the split point stack
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// 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;
// If we are here it means we are not available
assert(masterThread.state != Thread::AVAILABLE);
int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (i != master && threads[i].is_available_to(master))
{
threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
splitPoint.is_slave[i] = true;
workersCnt++;
}
assert(Fake || workersCnt > 1);
// 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)
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);
// We have returned from the idle loop, which means that all threads are // We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return. // finished. Note that setting is_searching and decreasing splitPointsCnt is
lock_grab(&mpLock); // done under lock protection to avoid a race with Thread::is_available_to().
sp.mutex.lock(); // To protect sp.nodes
mutex.lock();
*alpha = splitPoint.alpha; master->is_searching = true;
*bestValue = splitPoint.bestValue; master->splitPointsCnt--;
masterThread.activeSplitPoints--; master->curSplitPoint = sp.parent;
masterThread.splitPoint = splitPoint.parent; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
pos.set_nodes_searched(pos.nodes_searched() + splitPoint.nodes); *bestMove = sp.bestMove;
lock_release(&mpLock); mutex.unlock();
sp.mutex.unlock();
return sp.bestValue;
} }
// Explicit template instantiations // Explicit template instantiations
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool); template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool); template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
// set_timer() is used to set the timer to trigger after msec milliseconds.
// If msec is 0 then timer is stopped.
void ThreadPool::set_timer(int msec) {
timer->mutex.lock();
timer->maxPly = msec;
timer->sleepCondition.notify_one(); // Wake up and restart the timer
timer->mutex.unlock();
}
// wait_for_search_finished() waits for main thread to go to sleep, this means
// search is finished. Then returns.
void ThreadPool::wait_for_search_finished() {
Thread* t = main_thread();
t->mutex.lock();
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
while (!t->do_sleep) sleepCondition.wait(t->mutex);
t->mutex.unlock();
}
// start_searching() wakes up the main thread sleeping in main_loop() so to start
// a new search, then returns immediately.
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_search_finished();
SearchTime = Time::now(); // As early as possible
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
RootPosition = pos;
Limits = limits;
SetupStates = states; // Ownership transfer here
RootMoves.clear();
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (searchMoves.empty() || count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
main_thread()->do_sleep = false;
main_thread()->wake_up();
}
+95 -59
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,113 +20,149 @@
#if !defined(THREAD_H_INCLUDED) #if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <cstring> #include <vector>
#include "lock.h"
#include "material.h" #include "material.h"
#include "movepick.h" #include "movepick.h"
#include "pawns.h" #include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h"
const int MAX_THREADS = 32; const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8; const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex {
Mutex() { lock_init(l); }
~Mutex() { lock_destroy(l); }
void lock() { lock_grab(l); }
void unlock() { lock_release(l); }
private:
friend struct ConditionVariable;
Lock l;
};
struct ConditionVariable {
ConditionVariable() { cond_init(c); }
~ConditionVariable() { cond_destroy(c); }
void wait(Mutex& m) { cond_wait(c, m.l); }
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
void notify_one() { cond_signal(c); }
private:
WaitCondition c;
};
class Thread;
struct SplitPoint { struct SplitPoint {
// Const data after splitPoint has been setup // Const data after split point has been setup
SplitPoint* parent;
const Position* pos; const Position* pos;
const Search::Stack* ss;
Depth depth; Depth depth;
bool pvNode;
Value beta; Value beta;
int ply; int nodeType;
int master; Thread* master;
Move threatMove; Move threatMove;
// Const pointers to shared data // Const pointers to shared data
MovePicker* mp; MovePicker* mp;
SearchStack* ss; SplitPoint* parent;
// Shared data // Shared data
Lock lock; Mutex mutex;
volatile uint64_t slavesMask;
volatile int64_t nodes; volatile int64_t nodes;
volatile Value alpha; volatile Value alpha;
volatile Value bestValue; volatile Value bestValue;
volatile Move bestMove;
volatile int moveCount; volatile int moveCount;
volatile bool is_betaCutoff; volatile bool cutoff;
volatile bool is_slave[MAX_THREADS];
}; };
/// Thread struct is used to keep together all the thread related stuff like locks, /// Thread struct keeps together all the thread related stuff like locks, state
/// state and especially split points. We also use per-thread pawn and material hash /// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited and /// tables so that once we get a pointer to an entry its life time is unlimited
/// we don't have to care about someone changing the entry under our feet. /// and we don't have to care about someone changing the entry under our feet.
struct Thread { class Thread {
enum ThreadState typedef void (Thread::* Fn) (); // Pointer to member function
{
INITIALIZING, // Thread is initializing itself public:
SEARCHING, // Thread is performing work Thread(Fn fn);
AVAILABLE, // Thread is waiting for work ~Thread();
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(); void wake_up();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(int master) const; bool is_available_to(Thread* master) const;
void idle_loop();
void main_loop();
void timer_loop();
void wait_for_stop_or_ponderhit();
MaterialInfoTable materialTable; SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
PawnInfoTable pawnTable; MaterialTable materialTable;
PawnTable pawnTable;
size_t idx;
int maxPly; int maxPly;
Lock sleepLock; Mutex mutex;
WaitCondition sleepCond; ConditionVariable sleepCondition;
volatile ThreadState state; NativeHandle handle;
SplitPoint* volatile splitPoint; Fn start_fn;
volatile int activeSplitPoints; SplitPoint* volatile curSplitPoint;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS]; volatile int splitPointsCnt;
volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_exit;
}; };
/// ThreadsManager class is used to handle all the threads related stuff like init, /// ThreadPool class handles all the threads related stuff like init, starting,
/// starting, parking and, the most important, launching a slave thread at a split /// parking and, the most important, launching a slave thread at a split point.
/// point. All the access to shared thread data is done through this class. /// All the access to shared thread data is done through this class.
class ThreadPool {
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
need to explicitly initialize to zero its data members because variables with
static storage duration are automatically set to zero before enter main()
*/
public: public:
Thread& operator[](int threadID) { return threads[threadID]; } void init(); // No c'tor and d'tor, threads rely on globals that should
void init(); void exit(); // be initialized and valid during the whole thread lifetime.
void exit();
void init_hash_tables();
Thread& operator[](size_t id) { return *threads[id]; }
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; } int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; } size_t size() const { return threads.size(); }
void set_size(int cnt) { activeThreads = cnt; } Thread* main_thread() { return threads[0]; }
void wake_up() const;
void sleep() const;
void read_uci_options(); void read_uci_options();
bool available_slave_exists(int master) const; bool available_slave_exists(Thread* master) const;
void idle_loop(int threadID, SplitPoint* sp); void set_timer(int msec);
void wait_for_search_finished();
void start_searching(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&);
template <bool Fake> template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue, Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode); Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private: private:
Lock mpLock; friend class Thread;
std::vector<Thread*> threads;
Thread* timer;
Mutex mutex;
ConditionVariable sleepCondition;
Depth minimumSplitDepth; Depth minimumSplitDepth;
int maxThreadsPerSplitPoint; int maxThreadsPerSplitPoint;
bool useSleepingThreads; bool useSleepingThreads;
int activeThreads;
volatile bool allThreadsShouldExit;
Thread threads[MAX_THREADS];
}; };
extern ThreadsManager Threads; extern ThreadPool Threads;
#endif // !defined(THREAD_H_INCLUDED) #endif // !defined(THREAD_H_INCLUDED)
+27 -26
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@
*/ */
#include <cmath> #include <cmath>
#include <algorithm>
#include "misc.h"
#include "search.h" #include "search.h"
#include "timeman.h" #include "timeman.h"
#include "ucioption.h" #include "ucioption.h"
@@ -64,7 +64,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, 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 }; 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 /// Function Prototypes
@@ -72,18 +72,18 @@ namespace {
enum TimeType { OptimumTime, MaxTime }; enum TimeType { OptimumTime, MaxTime };
template<TimeType> template<TimeType>
int remaining(int myTime, int movesToGo, int currentPly); int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover);
} }
void TimeManager::pv_instability(int curChanges, int prevChanges) { void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2) unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3); + prevChanges * (optimumSearchTime / 3);
} }
void TimeManager::init(const SearchLimits& limits, int currentPly) void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
{ {
/* We support four different kind of time controls: /* We support four different kind of time controls:
@@ -103,51 +103,52 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
int hypMTG, hypMyTime, t1, t2; int hypMTG, hypMyTime, t1, t2;
// Read uci parameters // Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>(); int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"].value<int>(); int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"].value<int>(); int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"].value<int>(); int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"];
// Initialize to maximum values but unstablePVExtraTime that is reset // Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0; unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = limits.time; optimumSearchTime = maximumSearchTime = limits.time[us];
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the // 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. // 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 // Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time hypMyTime = limits.time[us]
+ limits.increment * (hypMTG - 1) + limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime - 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); t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly, slowMover);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly); t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly, slowMover);
optimumSearchTime = Min(optimumSearchTime, t1); optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2); maximumSearchTime = std::min(maximumSearchTime, t2);
} }
if (Options["Ponder"].value<bool>()) if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4; optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime // Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime); optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
} }
namespace { namespace {
template<TimeType T> template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly) int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
{ {
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio); const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly); int thisMoveImportance = move_importance(currentPly) * slowMover / 100;
int otherMovesImportance = 0; int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++) for (int i = 1; i < movesToGo; i++)
@@ -156,6 +157,6 @@ namespace {
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance); float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(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)));
} }
} }
+4 -4
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,12 +20,12 @@
#if !defined(TIMEMAN_H_INCLUDED) #if !defined(TIMEMAN_H_INCLUDED)
#define 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 { class TimeManager {
public: public:
void init(const Search::LimitsType& limits, int currentPly, Color us);
void init(const SearchLimits& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges); void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; } int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; } int maximum_time() const { return maximumSearchTime; }
+12 -15
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,10 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include "bitboard.h"
#include "tt.h" #include "tt.h"
TranspositionTable TT; // Our global transposition table TranspositionTable TT; // Our global transposition table
@@ -38,18 +38,13 @@ TranspositionTable::~TranspositionTable() {
/// TranspositionTable::set_size() sets the size of the transposition table, /// TranspositionTable::set_size() sets the size of the transposition table,
/// measured in megabytes. /// measured in megabytes. Transposition table consists of a power of 2 number of
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each
/// non-empty entry contains information of exactly one position.
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1024; size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster));
// Transposition table consists of clusters and each cluster consists
// of ClusterSize number of TTEntries. Each non-empty entry contains
// information of exactly one position and newSize is the number of
// clusters we are going to allocate.
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2;
if (newSize == size) if (newSize == size)
return; return;
@@ -57,13 +52,15 @@ void TranspositionTable::set_size(size_t mbSize) {
size = newSize; size = newSize;
delete [] entries; delete [] entries;
entries = new (std::nothrow) TTCluster[size]; entries = new (std::nothrow) TTCluster[size];
if (!entries) if (!entries)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl; << "MB for transposition table." << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear();
clear(); // Operator new is not guaranteed to initialize memory to zero
} }
@@ -85,7 +82,7 @@ void TranspositionTable::clear() {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2. /// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3; int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
@@ -107,7 +104,7 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
// Implement replace strategy // Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0); c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == VALUE_TYPE_EXACT ? -2 : 0); c2 = (tte->generation() == generation || tte->type() == BOUND_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0); c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
+7 -44
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,12 +20,9 @@
#if !defined(TT_H_INCLUDED) #if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED #define TT_H_INCLUDED
#include <iostream> #include "misc.h"
#include "move.h"
#include "types.h" #include "types.h"
/// The TTEntry is the class of transposition table entries /// The TTEntry is the class of transposition table entries
/// ///
/// A TTEntry needs 128 bits to be stored /// A TTEntry needs 128 bits to be stored
@@ -47,11 +44,11 @@
class TTEntry { class TTEntry {
public: public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value statM) { void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value statV, Value statM) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
valueType = (uint8_t)t; bound = (uint8_t)b;
generation8 = (uint8_t)g; generation8 = (uint8_t)g;
value16 = (int16_t)v; value16 = (int16_t)v;
depth16 = (int16_t)d; depth16 = (int16_t)d;
@@ -64,7 +61,7 @@ public:
Depth depth() const { return (Depth)depth16; } Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; } Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
ValueType type() const { return (ValueType)valueType; } Bound type() const { return (Bound)bound; }
int generation() const { return (int)generation8; } int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; } Value static_value() const { return (Value)staticValue; }
Value static_value_margin() const { return (Value)staticMargin; } Value static_value_margin() const { return (Value)staticMargin; }
@@ -72,7 +69,7 @@ public:
private: private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t valueType, generation8; uint8_t bound, generation8;
int16_t value16, depth16, staticValue, staticMargin; int16_t value16, depth16, staticValue, staticMargin;
}; };
@@ -103,7 +100,7 @@ public:
~TranspositionTable(); ~TranspositionTable();
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD); void store(const Key posKey, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
TTEntry* probe(const Key posKey) const; TTEntry* probe(const Key posKey) const;
void new_search(); void new_search();
TTEntry* first_entry(const Key posKey) const; TTEntry* first_entry(const Key posKey) const;
@@ -136,38 +133,4 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation); const_cast<TTEntry*>(tte)->set_generation(generation);
} }
/// A simple fixed size hash table used to store pawns and material
/// configurations. It is basically just an array of Entry objects.
/// Without cluster concept or overwrite policy.
template<class Entry, int HashSize>
struct SimpleHash {
typedef SimpleHash<Entry, HashSize> Base;
void init() {
if (entries)
return;
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
<< " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
}
virtual ~SimpleHash() { delete [] entries; }
Entry* probe(Key key) const { return entries + ((uint32_t)key & (HashSize - 1)); }
void prefetch(Key key) const { ::prefetch((char*)probe(key)); }
protected:
Entry* entries;
};
#endif // !defined(TT_H_INCLUDED) #endif // !defined(TT_H_INCLUDED)
+299 -276
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,152 +20,138 @@
#if !defined(TYPES_H_INCLUDED) #if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get
/// started type 'make help'.
///
/// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Use always.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
#include <cctype>
#include <climits> #include <climits>
#include <cstdlib> #include <cstdlib>
#if defined(_MSC_VER) #include "platform.h"
// 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
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h>
#endif
#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) #if defined(_WIN64)
#define IS_64BIT # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#endif #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) #if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
#include <nmmintrin.h> # include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif #endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64)) # define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else #else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) # define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif #endif
// Define a __cpuid() function for gcc compilers, for Intel and MSVC
// is already available as an intrinsic.
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <intrin.h> # define FORCE_INLINE __forceinline
#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
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline)) # define FORCE_INLINE inline __attribute__((always_inline))
#else #else
#define FORCE_INLINE inline # define FORCE_INLINE inline
#endif #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) #if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt(); const bool HasPopCnt = true;
#else #else
const bool CpuHasPOPCNT = false; const bool HasPopCnt = false;
#endif #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) #if defined(IS_64BIT)
const bool CpuIs64Bit = true; const bool Is64Bit = true;
#else #else
const bool CpuIs64Bit = false; const bool Is64Bit = false;
#endif #endif
#include <string>
typedef uint64_t Key; typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int PLY_MAX = 100; const int MAX_MOVES = 192;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2; const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
enum ValueType { const Bitboard FileABB = 0x0101010101010101ULL;
VALUE_TYPE_NONE = 0, const Bitboard FileBBB = FileABB << 1;
VALUE_TYPE_UPPER = 1, const Bitboard FileCBB = FileABB << 2;
VALUE_TYPE_LOWER = 2, const Bitboard FileDBB = FileABB << 3;
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
enum MoveType {
NORMAL = 0,
PROMOTION = 1 << 14,
ENPASSANT = 2 << 14,
CASTLE = 3 << 14
};
enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0,
WHITE_OO = 1,
WHITE_OOO = 2,
BLACK_OO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum Bound {
BOUND_NONE = 0,
BOUND_UPPER = 1,
BOUND_LOWER = 2,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
}; };
enum Value { enum Value {
@@ -176,34 +162,44 @@ enum Value {
VALUE_INFINITE = 30001, VALUE_INFINITE = 30001,
VALUE_NONE = 30002, VALUE_NONE = 30002,
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
Mg = 0, Eg = 1,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1278,
QueenValueMg = 2521, QueenValueEg = 2558
}; };
enum PieceType { enum PieceType {
PIECE_TYPE_NONE = 0, NO_PIECE_TYPE = 0, ALL_PIECES = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
}; };
enum Piece { enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6, NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16 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 { enum Color {
WHITE, BLACK, COLOR_NONE WHITE, BLACK, NO_COLOR
}; };
enum Depth { enum Depth {
ONE_PLY = 2, ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY, DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };
@@ -240,42 +236,54 @@ enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
}; };
enum SquareColor {
DARK, LIGHT
};
enum ScaleFactor { /// Score enum keeps a midgame and an endgame value in a single integer (enum),
SCALE_FACTOR_ZERO = 0, /// first LSB 16 bits are used to store endgame value, while upper bits are used
SCALE_FACTOR_NORMAL = 64, /// for midgame value. Compiler is free to choose the enum type as long as can
SCALE_FACTOR_MAX = 128, /// keep its data, so ensure Score to be an integer type.
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.
enum Score { enum Score {
SCORE_ZERO = 0, SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
#define ENABLE_OPERATORS_ON(T) \ inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
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)); } \ /// Extracting the signed lower and upper 16 bits it not so trivial because
inline T operator* (int i, const T d) { return T(i * int(d)); } \ /// according to the standard a simple cast to short is implementation defined
inline T operator* (const T d, int i) { return T(int(d) * i); } \ /// and so is a right shift of a signed integer.
inline T operator/ (const T d, int i) { return T(int(d) / i); } \ inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
inline T operator- (const T d) { return T(-int(d)); } \
inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \ /// On Intel 64 bit we have a small speed regression with the standard conforming
inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \ /// version, so use a faster code in this case that, although not 100% standard
inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \ /// compliant it seems to work for Intel and MSVC.
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \ #if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
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 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(Value)
ENABLE_OPERATORS_ON(PieceType) ENABLE_OPERATORS_ON(PieceType)
@@ -286,185 +294,200 @@ ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File) ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank) 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 ENABLE_SAFE_OPERATORS_ON(Score)
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
// Extracting the _signed_ lower and upper 16 bits it not so trivial /// Only declared but not defined. We don't want to multiply two scores due to
// because according to the standard a simple cast to short is /// a very high risk of overflow. So user should explicitly convert to integer.
// 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.
inline Score operator*(Score s1, Score s2); inline Score operator*(Score s1, Score s2);
// Remaining Score operators are standard /// Division of a Score must be handled separately for each term
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); } inline Score operator/(Score s, int i) {
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); } return make_score(mg_value(s) / i, eg_value(s) / i);
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); }
const Value PawnValueMidgame = Value(0x0C6); /// Weight score v by score w trying to prevent overflow
const Value PawnValueEndgame = Value(0x102); inline Score apply_weight(Score v, Score w) {
const Value KnightValueMidgame = Value(0x331); return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
const Value KnightValueEndgame = Value(0x34E); (int(eg_value(v)) * eg_value(w)) / 0x100);
const Value BishopValueMidgame = Value(0x344); }
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
inline Value value_mate_in(int ply) { #undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
namespace Zobrist {
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
extern Key enpassant[8]; // [file]
extern Key castle[16]; // [castleRight]
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[16][64]; // [piece][square]
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType]
extern int SquareDistance[64][64]; // [square][square]
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
inline Color operator~(Color c) {
return Color(c ^ 1);
}
inline Square operator~(Square s) {
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
}
inline Square operator|(File f, Rank r) {
return Square((r << 3) | f);
}
inline Value mate_in(int ply) {
return VALUE_MATE - ply; return VALUE_MATE - ply;
} }
inline Value value_mated_in(int ply) { inline Value mated_in(int ply) {
return -VALUE_MATE + ply; return -VALUE_MATE + ply;
} }
inline Piece make_piece(Color c, PieceType pt) { 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) { inline CastleRight make_castle_right(Color c, CastlingSide s) {
return PieceType(int(p) & 7); return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
} }
inline Color color_of_piece(Piece p) { inline PieceType type_of(Piece p) {
return Color(int(p) >> 3); return PieceType(p & 7);
} }
inline Color opposite_color(Color c) { inline Color color_of(Piece p) {
return Color(int(c) ^ 1); return Color(p >> 3);
} }
inline bool color_is_ok(Color c) { inline bool is_ok(Square s) {
return c == WHITE || c == BLACK; return s >= SQ_A1 && s <= SQ_H8;
} }
inline bool piece_type_is_ok(PieceType pt) { inline File file_of(Square s) {
return pt >= PAWN && pt <= KING; return File(s & 7);
} }
inline bool piece_is_ok(Piece p) { inline Rank rank_of(Square s) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p)); return Rank(s >> 3);
} }
inline char piece_type_to_char(PieceType pt) { inline Square mirror(Square s) {
static const char ch[] = " PNBRQK"; return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1
return ch[pt];
}
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) { inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 56)); return Square(s ^ (c * 56));
} }
inline Rank relative_rank(Color c, Rank r) { inline Rank relative_rank(Color c, Rank r) {
return Rank(int(r) ^ (int(c) * 7)); return Rank(r ^ (c * 7));
} }
inline Rank relative_rank(Color c, Square s) { inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, square_rank(s)); return relative_rank(c, rank_of(s));
} }
inline SquareColor square_color(Square s) { inline bool opposite_colors(Square s1, Square s2) {
return SquareColor(int(square_rank(s) + s) & 1);
}
inline bool opposite_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2); int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1; return ((s >> 3) ^ s) & 1;
} }
inline int file_distance(Square s1, Square s2) { inline int file_distance(Square s1, Square s2) {
return abs(square_file(s1) - square_file(s2)); return abs(file_of(s1) - file_of(s2));
} }
inline int rank_distance(Square s1, Square s2) { inline int rank_distance(Square s1, Square s2) {
return abs(square_rank(s1) - square_rank(s2)); return abs(rank_of(s1) - rank_of(s2));
} }
inline int square_distance(Square s1, Square s2) { inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2)); return SquareDistance[s1][s2];
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
} }
inline char file_to_char(File f) { inline char file_to_char(File f) {
return char(f - FILE_A + int('a')); 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) { inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1')); 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;
}
inline bool square_is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline Square pawn_push(Color c) { inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S; return c == WHITE ? DELTA_N : DELTA_S;
} }
inline Square from_sq(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square to_sq(Move m) {
return Square(m & 0x3F);
}
inline MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
inline PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ;
}
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
}
#include <string>
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 };
return ch;
}
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K first, K last)
{
T tmp;
K p, q;
for (p = first + 1; p < last; p++)
{
tmp = *p;
for (q = p; q != first && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)
+183 -179
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,231 +17,235 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "evaluate.h" #include "evaluate.h"
#include "misc.h" #include "notation.h"
#include "move.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
extern void benchmark(const Position& pos, istream& is);
namespace { namespace {
// FEN string for the initial position // FEN string of the initial position, normal chess
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 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 // Keep track of position keys along the setup moves (from start position to the
// is actually a string stream built on a given input string. // position just before to start searching). Needed by repetition draw detection.
typedef istringstream UCIParser; Search::StateStackPtr SetupStates;
void set_option(UCIParser& up); void set_option(istringstream& up);
void set_position(Position& pos, UCIParser& up); void set_position(Position& pos, istringstream& up);
bool go(Position& pos, UCIParser& up); void go(Position& pos, istringstream& up);
void perft(Position& pos, UCIParser& up);
} }
/// execute_uci_command() takes a string as input, uses a UCIParser /// Wait for a command from the user, parse this text string as an UCI command,
/// object to parse this text string as a UCI command, and calls /// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// the appropriate functions. In addition to the UCI commands, /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// the function also supports a few debug commands. /// commands, the function also supports a few debug commands.
bool execute_uci_command(const string& cmd) { void UCI::loop(const string& args) {
static Position pos(StartPositionFEN, false, 0); // The root position Position pos(StartFEN, false, Threads.main_thread()); // The root position
string cmd, token;
UCIParser up(cmd); while (token != "quit")
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")
{ {
read_evaluation_uci_options(pos.side_to_move()); if (!args.empty())
cout << trace_evaluate(pos) << endl; cmd = args;
else if (!getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop")
{
Search::Signals.stop = true;
Threads.wait_for_search_finished(); // Cannot quit while threads are running
}
else if (token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main_thread()->wake_up(); // Could be sleeping
}
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
{ /* Avoid returning "Unknown command" */ }
else if (token == "isready")
sync_cout << "readyok" << sync_endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "bench")
benchmark(pos, is);
else if (token == "key")
sync_cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "perft" && (is >> token)) // Read depth
{
stringstream ss;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << token << " current perft";
benchmark(pos, ss);
}
else
sync_cout << "Unknown command: " << cmd << sync_endl;
if (!args.empty()) // Command line arguments have one-shot behaviour
{
Threads.wait_for_search_finished();
break;
}
} }
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;
} }
namespace { namespace {
// set_position() is called when engine receives the "position" UCI // set_position() is called when engine receives the "position" UCI command.
// command. The function sets up the position described in the given // The function sets up the position described in the given fen string ("fen")
// fen string ("fen") or the starting position ("startpos") and then // or the starting position ("startpos") and then makes the moves given in the
// makes the moves given in the following move list ("moves"). // following move list ("moves").
void set_position(Position& pos, UCIParser& up) { void set_position(Position& pos, istringstream& is) {
Move m;
string token, fen; string token, fen;
up >> token; // operator>>() skips any whitespace is >> token;
if (token == "startpos") if (token == "startpos")
{ {
pos.from_fen(StartPositionFEN, false); fen = StartFEN;
up >> token; // Consume "moves" token if any is >> token; // Consume "moves" token if any
} }
else if (token == "fen") else if (token == "fen")
{ while (is >> token && token != "moves")
while (up >> token && token != "moves")
fen += token + " "; fen += token + " ";
else
return;
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>()); pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
} SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
else return;
// Parse move list (if any) // Parse move list (if any)
while (up >> token) while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
pos.do_setup_move(move_from_uci(pos, token));
}
// set_option() is called when engine receives the "setoption" UCI
// command. The function updates the corresponding UCI option ("name")
// to the given value ("value").
void set_option(UCIParser& up) {
string token, name;
string value = "true"; // UCI buttons don't have a "value" field
up >> token; // Consume "name" token
up >> name; // Read option name
// Handle names with included spaces
while (up >> token && token != "value")
name += " " + token;
up >> value; // Read option value
// Handle values with included spaces
while (up >> token)
value += " " + token;
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
cout << "No such option: " << name << endl;
}
// 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.
bool go(Position& pos, UCIParser& up) {
string token;
SearchLimits limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token)
{ {
if (token == "infinite") SetupStates->push(StateInfo());
pos.do_move(m, SetupStates->top());
}
}
// 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(istringstream& is) {
string token, name, value;
is >> token; // Consume "name" token
// Read option name (can contain spaces)
while (is >> token && token != "value")
name += string(" ", !name.empty()) + token;
// Read option value (can contain spaces)
while (is >> token)
value += string(" ", !value.empty()) + token;
if (Options.count(name))
Options[name] = value;
else
sync_cout << "No such option: " << name << sync_endl;
}
// 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 search.
void go(Position& pos, istringstream& is) {
Search::LimitsType limits;
vector<Move> searchMoves;
string token;
while (is >> token)
{
if (token == "wtime")
is >> limits.time[WHITE];
else if (token == "btime")
is >> limits.time[BLACK];
else if (token == "winc")
is >> limits.inc[WHITE];
else if (token == "binc")
is >> limits.inc[BLACK];
else if (token == "movestogo")
is >> limits.movestogo;
else if (token == "depth")
is >> limits.depth;
else if (token == "nodes")
is >> limits.nodes;
else if (token == "movetime")
is >> limits.movetime;
else if (token == "infinite")
limits.infinite = true; limits.infinite = true;
else if (token == "ponder") else if (token == "ponder")
limits.ponder = true; limits.ponder = true;
else if (token == "wtime")
up >> time[WHITE];
else if (token == "btime")
up >> time[BLACK];
else if (token == "winc")
up >> inc[WHITE];
else if (token == "binc")
up >> inc[BLACK];
else if (token == "movestogo")
up >> limits.movesToGo;
else if (token == "depth")
up >> limits.maxDepth;
else if (token == "nodes")
up >> limits.maxNodes;
else if (token == "movetime")
up >> limits.maxTime;
else if (token == "searchmoves") else if (token == "searchmoves")
while (up >> token) while (is >> token)
*cur++ = move_from_uci(pos, token); searchMoves.push_back(move_from_uci(pos, token));
} }
*cur = MOVE_NONE; Threads.start_searching(pos, limits, searchMoves, SetupStates);
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok());
return think(pos, limits, searchMoves);
}
// 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.
void perft(Position& pos, UCIParser& up) {
int depth, time;
int64_t n;
if (!(up >> depth))
return;
time = get_system_time();
n = perft(pos, depth * ONE_PLY);
time = get_system_time() - time;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << time
<< "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
} }
} }
+103 -107
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,154 +17,150 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cctype> #include <algorithm>
#include <iostream> #include <cassert>
#include <cstdlib>
#include <sstream> #include <sstream>
#include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
using std::cout;
using std::endl;
OptionsMap Options; // Global object UCI::OptionsMap Options; // Global object
namespace UCI {
/// 'On change' actions, triggered by an option's value change
void on_logger(const Option& o) { start_logger(o); }
void on_eval(const Option&) { Eval::init(); }
void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.set_size(o); }
void on_clear_hash(const Option&) { TT.clear(); }
// Our case insensitive less() function as required by UCI protocol /// Our case insensitive less() function as required by UCI protocol
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
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 /// init() initializes the UCI options to their hard coded default values
template<typename T> /// and initializes the default value of "Threads" and "Min Split Depth"
static string stringify(const T& v) { /// parameters according to the number of CPU cores detected.
std::ostringstream ss; void init(OptionsMap& o) {
ss << v;
return ss.str(); int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
o["Use Debug Log"] = Option(false, on_logger);
o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 7, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(false, on_threads);
o["Hash"] = Option(32, 4, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true);
o["OwnBook"] = Option(false);
o["MultiPV"] = Option(1, 1, 500);
o["Skill Level"] = Option(20, 0, 20);
o["Emergency Move Horizon"] = Option(40, 0, 50);
o["Emergency Base Time"] = Option(200, 0, 30000);
o["Emergency Move Time"] = Option(70, 0, 5000);
o["Minimum Thinking Time"] = Option(20, 0, 5000);
o["Slow Mover"] = Option(100, 10, 1000);
o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false, on_eval);
} }
/// OptionsMap c'tor initializes the UCI options to their hard coded default /// operator<<() is used to print all the options default values in chronological
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
OptionsMap::OptionsMap() {
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);
}
/// 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. /// insertion order (the idx field) and in the format defined by the UCI protocol.
string OptionsMap::print_all() const { std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
std::stringstream s; for (size_t idx = 0; idx < om.size(); idx++)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
for (size_t i = 0; i <= size(); i++) if (it->second.idx == idx)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
{ {
const UCIOption& o = it->second; const Option& o = it->second;
s << "\noption name " << it->first << " type " << o.type; os << "\noption name " << it->first << " type " << o.type;
if (o.type != "button") if (o.type != "button")
s << " default " << o.defaultValue; os << " default " << o.defaultValue;
if (o.type == "spin") if (o.type == "spin")
s << " min " << o.minValue << " max " << o.maxValue; os << " min " << o.min << " max " << o.max;
break; break;
} }
return s.str(); return os;
} }
/// Option class c'tors /// Option c'tors and conversion operators
UCIOption::UCIOption(const char* def) : type("string"), minValue(0), maxValue(0), idx(Options.size()) Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f)
{ defaultValue = currentValue = def; } { defaultValue = currentValue = v; }
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size()) Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f)
{ defaultValue = currentValue = (def ? "true" : "false"); } { defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size()) Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f)
{ defaultValue = currentValue = stringify(def); } {}
Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f)
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// set_value() updates currentValue of the Option object. Normally it's up to Option::operator int() const {
/// the GUI to check for option's limits, but we could receive the new value assert(type == "check" || type == "spin");
/// directly from the user by teminal window. So let's check the bounds anyway. return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
void UCIOption::set_value(const string& v) { Option::operator std::string() const {
assert(type == "string");
return currentValue;
}
/// operator=() updates currentValue and triggers on_change() action. It's up to
/// the GUI to check for option's limits, but we could receive the new value from
/// the user by console window, so let's check the bounds anyway.
Option& Option::operator=(const string& v) {
assert(!type.empty()); assert(!type.empty());
if (v.empty()) if ( (type != "button" && v.empty())
return; || (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
return *this;
if ((type == "check" || type == "button") != (v == "true" || v == "false")) if (type != "button")
return; currentValue = v;
if (type == "spin") if (on_change)
{ (*on_change)(*this);
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = v; return *this;
} }
} // namespace UCI
+31 -47
View File
@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,66 +20,50 @@
#if !defined(UCIOPTION_H_INCLUDED) #if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <cassert>
#include <cstdlib>
#include <map> #include <map>
#include <string> #include <string>
class UCIOption { namespace UCI {
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);
void set_value(const std::string& v); class Option;
template<typename T> T value() const;
private: /// Custom comparator because UCI options should be case insensitive
friend class OptionsMap;
std::string defaultValue, currentValue, type;
int minValue, maxValue;
size_t idx;
};
/// Custom comparator because UCI options should not be case sensitive
struct CaseInsensitiveLess { struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const; bool operator() (const std::string&, const std::string&) const;
}; };
/// Our options container is actually a std::map
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
/// Option class implements an option as defined by UCI protocol
class Option {
typedef void (Fn)(const Option&);
/// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public: public:
OptionsMap(); Option(Fn* = NULL);
std::string print_all() const; Option(bool v, Fn* = NULL);
Option(const char* v, Fn* = NULL);
Option(int v, int min, int max, Fn* = NULL);
Option& operator=(const std::string& v);
operator int() const;
operator std::string() const;
private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int min, max;
size_t idx;
Fn* on_change;
}; };
extern OptionsMap Options; void init(OptionsMap&);
void loop(const std::string&);
} // namespace UCI
/// Option::value() definition and specializations extern UCI::OptionsMap Options;
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) #endif // !defined(UCIOPTION_H_INCLUDED)