Compare commits

..

143 Commits

Author SHA1 Message Date
Marco Costalba
aa2de53a83 Stockfish 2.0 (take 2)
Always same siganture: 7224363

Hopefully some more bug fixed and restored
compatibility with WIndows XP.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:10:30 +01:00
Marco Costalba
f826923f8e Don't use SRWLOCK and Condition Variables under Windows
They are not compatible with Windows XP

Revert to old CRITICAL_SECTION locks and events.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:32 +01:00
Marco Costalba
3201a43460 Fix an off-by-one bug in sort_multipv()
Second parameter of insertion_sort() is a pointer to the
element _after_ the last of the list, e.g. end() when sorting
all items.

If we want to sort say the first 2 moves we should write:

sort_multipv(2);

So, becuase in root moves loop move counter 'i' starts
from 0, we need to pass:

sort_multipv(i+1);

To sort up to move 'i' included.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:30 +01:00
Marco Costalba
5405efabcb Remove artificial Iteration >= 3 constraint on time manager
It doesn't seem to have any meaning.

Also add a FIXME on the MaxNodes condition that now is broken
in SMP case due to known issue with pos.nodes_searched()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:29 +01:00
Marco Costalba
6df86fc9da Fix: Honour UCI "quit" command while still in the book
We were not quitting the engine after a "quit" command
while still in the book and pondering.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:28 +01:00
Marco Costalba
7315001f37 Simplify "ponderhit" handling
If flag StopOnPonderhit is set it means that we UseTimeManagement
and also we are at Iteration >= 3.

So we can safely simplify the formula.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:26 +01:00
Marco Costalba
191662a159 Retire ponderhit()
It is called only from one place, so move code there.

Add a bit of renaming and documentation while at there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 16:07:25 +01:00
Marco Costalba
a01df59f5e Restore development version
And set "Use Sleeping Threads" to true because it keeps
much more responsive and cool my QUAD during tests :-)

It will be reverted back before to release that's the
reason to bundle it here.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 12:30:03 +01:00
Marco Costalba
9f3c7ded5c Stockfish 2.0
stockfish bench signature is: 7224363

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-01-01 11:47:34 +01:00
Marco Costalba
df73af30dd Send correct searched nodes statistic
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-31 19:47:17 +01:00
Marco Costalba
9394db765f Remove dubious castle detector
It was introduced by patch 66d16592 of 22/3/2009 merging
from Glarurung iPhone.

Tord says:
That change is only found in the Glaurung iPhone app, and not
in the latest Glaurung UCI source code. I don't remember why
this was added (and the iPhone app, unlike the UCI engine,
was never version controlled), but it was almost certainly
because it was somehow needed in the communication between
the engine and the iPhone GUI, and that it was never meant to be
included in the UCI engine. My guess is that it has something to
do with castling moves being entered as e1-g1 in the GUI, but
represented as e1-h1 in the chess engine.

Removing it in Stockfish should be completely safe, and won't harm
the iPhone version. Initially the iPhone GUI called functions in the
chess engine for checking for legality of moves, writing the move
list in SAN format, and various other tasks, but this is no longer
the case in the current version.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-31 14:32:25 +01:00
Marco Costalba
4d6258bb32 Implement "seldepth" UCI info
This is the "selective search depth in plies" and we set
equal to PV line length.

Tested that works under FritzGUI.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-31 14:27:24 +01:00
Marco Costalba
3e8bb6f63d Retire a couple of unused debug functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 16:44:10 +01:00
Marco Costalba
3835f49aa1 Leave threads go to sleep when waiting for a ponderhit
No need to heat up CPU in this case ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 16:18:22 +01:00
Marco Costalba
8e75384dd2 Move printing of best move on think
It seems a more appropiate place (IMHO) and helps to clarify
that idle_loop() should return a move, not a score.

Fix also handling of stalemate positions (we were not
sending any score) and we don't need to wait on "ponderhit",
this is done when returning in think().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 16:12:51 +01:00
Marco Costalba
db4a68a99b Unify single and multi PV 'new best move' handling
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 14:03:08 +01:00
Marco Costalba
f803f33e63 Move print_pv_info() under RootMove
And rename to pv_info_to_uci()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 12:27:59 +01:00
Marco Costalba
6f2e4c006c Better document value_to_uci()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 11:46:53 +01:00
Marco Costalba
afe0203f98 Standardize root_search() signature
Now pass alpha and beta by copy as in serach()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-30 10:54:33 +01:00
Marco Costalba
b01e5fc612 Move extract_pv_from_tt() and insert_pv_in_tt() under RootMove
Functional change only due to additional do/undo move
but absolutly harmless.

Also handle re-insertion in tt of PV lines also for multi PV case.
2010-12-29 15:24:40 +01:00
Marco Costalba
d2a4aac53d Use rml[0].pv[] instead of dedicated pv[] array
We have a small functionality change in case we have a
fail-high so that both rml[].pv and pv[] are updated, but if,
after researching, we have a fail-low then rml score is updated
again but pv[] remains the same and coming back from search we
used a PV line that has failed-low (after having failed-high).

With this patch we always use the 'correct' PV line, i.e. the
line with highest score at the end of the whole search.

Retire also redundant RootMove's 'move' member and directly
use pv[0] instead.
2010-12-29 09:22:30 +01:00
Marco Costalba
58c6e64069 Last small touches in RootMoveList
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-28 23:10:21 +01:00
Marco Costalba
3346ccc9d9 Retire LMR intermediate research
It does not seem to improve anything.

After 8344 games Mod - Orig:
1362 - 1321 - 5661 ELO +2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-28 18:51:08 +01:00
Marco Costalba
0007b43a91 Use insertion_sort() in RootMoveList
Simplify code and get a bit of extra speed, about +0.5%

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-28 18:36:34 +01:00
Marco Costalba
6235904898 Redefine MoveStack comparison as the natural one
Define symbol '<' to mean 'minor of', as it should be. Its meaning
was reversed to be used with std::sort() that sorts in ascending order
while we want a descending order.

But now that we use our own sorting code we don't need this
trick anymore.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-28 13:06:53 +01:00
Marco Costalba
5b08494312 Fix broken last patch series
When a reference breaks things !

Here we take a reference (that is a pointer) to an
entry in a vector that changes below us --> BOOM !

References are essential but should be considered with
care in C++ because could lead to nasty surprises.

Restored functionality.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-28 13:01:14 +01:00
Marco Costalba
30c14fdc95 Speedup moves root list sorting
Instead of a default member by member copy use set_pv()
to copy the useful part of pv[] array and skip the remaining.

This greatly speeds up sorting of root move list !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-27 13:55:34 +01:00
Marco Costalba
e8b5420300 Use a std::vector to store root moves
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-27 13:10:32 +01:00
Marco Costalba
12eb27b6e9 Rename RootMoveList members removing 'move'
It is redundant being a move list ;-)

Also better document the two scores used by root list.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-27 11:36:57 +01:00
Marco Costalba
e7ab3a0d16 Retire DirectionTable[]
With this patch even word 'direction' is disappeared !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-27 03:08:10 +01:00
Marco Costalba
4dded4e72f Retire direction.cpp
Move the code to bitboard.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-26 16:51:21 +01:00
Marco Costalba
cb7f20913e Retire enum Direction
Use SquareDelta instead

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-26 16:17:51 +01:00
Marco Costalba
f08a6eed0d Retire SignedDirectionTable[] and RayBB[]
Function ray_bb() was used just in one endgame where can
be used squares_in_front_of() instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-26 12:12:58 +01:00
Marco Costalba
6080fecf9c Retire direction.h
Move all to square.h

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-26 10:58:52 +01:00
Marco Costalba
94a67c49b0 Simplify enum SquareDelta definition
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-26 10:43:22 +01:00
Marco Costalba
0a73a23dc9 Unify MovePicker c'tor call
And use the standard one.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-25 18:03:43 +01:00
Marco Costalba
a0f0a7dc4f Use generate_moves() in san.cpp
Instead of MovePicker and cleanup san.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-25 17:54:08 +01:00
Marco Costalba
61c03b9d22 Ignore two braindamaged remarks under icc
Remark 1418: external function definition with no prior declaration

and

Remark 1419: external declaration in primary source file

Can be safely ignored because are pure idiocy.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-25 09:16:30 +01:00
Marco Costalba
dee8780829 Renamed thread_should_stop() in cutoff_at_splitpoint()
It is more clear what happened.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-25 00:11:53 +01:00
Marco Costalba
d40a12f948 Simplify a condition in update of best move
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-24 17:09:39 +01:00
Marco Costalba
f97c5b6909 Triviality in struct PieceLetters
And little touches in search() too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-24 16:58:25 +01:00
Marco Costalba
d55a5a4d81 Better clarify how we use TT depth in qsearch
Namely we use only two types of depth in TT:
DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-18 11:19:31 +01:00
Marco Costalba
201e8d5f87 Second cleanup wave on check_is_useless()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-18 10:20:15 +01:00
Joona Kiiski
47f5560e2d Let check_is_useless() follow SF coding style
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-14 18:57:22 +01:00
Tord Romstad
cf85ffbb97 Fixed a bug in move_from_uci(): En passant captures were not handled
correctly.
2010-12-14 12:07:37 +01:00
Joona Kiiski
6596dc9516 Selective checks at qsearch
After 5821 games
Mod- Orig:  1014 - 869 - 3938 ELO +8 (+- 3.6) LOS 97%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-14 07:50:37 +01:00
Marco Costalba
6afcfd00f2 Retire uci_main_loop()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-13 20:38:41 +01:00
Marco Costalba
2d63f2157e Small cleanup in uci.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-13 20:29:40 +01:00
Marco Costalba
56de5ae561 Retire square_from_string()
And rename move_from/to_string() in a more specific
move_from/to_uci() that is a simple coordinate notation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-13 20:29:38 +01:00
Marco Costalba
556b63b6b6 Move the last multi-threads globals to ThreadsManager
Also rename ThreadsManager memeber data to be lower case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-13 07:30:59 +01:00
Marco Costalba
4ad03c9bad Further reduce sleep lock contention
Use one sleep lock per thread insted of a single one shared.

Also renamed WaitLock in SleepLock.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-13 07:30:59 +01:00
Marco Costalba
dedc6d7588 Allow threads to sleep when available
By mean of an an UCI option it is possible let the available
threads to sleep, this should help with Hyper Threading although
is not the best solution when number of threads equals number
of available cores.

Option is disabled by default.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-11 09:22:38 +01:00
Marco Costalba
38e7ec3e44 Increase MAX_THREADS to 16
No speed regression and no functional change.

After 7826 games Mod- Orig:
1188 - 1230 - 5408 ELO -1 (+- 3.1)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-10 07:24:48 +01:00
Marco Costalba
421f7b74c6 Increase PV LMR to SF 1.8 levels
Non-PV LMR is left unchanged.

After 8819 games
Mod- Orig:  1442 - 1343 - 6034 ELO +3 (+- 2.9) LOS 86%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-07 13:13:06 +01:00
Marco Costalba
0da461f23b Various cleanups in Position's ancillary functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-06 18:01:18 +01:00
Marco Costalba
97a6e1559e Fix a crash due to a broken Book::open()
Bug introduced in 9dcc2aad98

We can be asked to open a non-exsistent file,
in this case we should gracefully handle the
case and silently return instead of exiting.

Bug discovered and bisected down by Joona.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-05 09:24:18 +01:00
Joona Kiiski
8a858aea34 New try for unstoppable pawn evaluation
This time we try very hard to avoid false positives.
The obvious downside is that we also miss many true
winning positions.

After 10544 games on RC
Mod- Orig:  1744  - 1646 - 7154 ELO +3 (+- 2.7) LOS 83%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-04 09:07:45 +01:00
Marco Costalba
03dd1d3c13 Allow razoring after a null move
After 8322 games on Russian Cluster
Mod- Orig:  1341 - 1281 - 5700 ELO +2 (+- 3) LOS 75%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-04 09:04:54 +01:00
Joona Kiiski
5529706426 Prune all negative see moves at low depths
After 2036 games Mod- Orig:
381 - 278 - 1377 ELO +17 (+- 6.2) LOS 99%

One of the biggest increases ever !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-12-02 07:42:18 +01:00
Marco Costalba
6ed409ecee Fix bestmove output in multi PV case
When MultiPV > 1, always take bestmove from the RootMoveList
(and don't bother with a ponder move). Without that the bestmove
is most probably incorrect.

Patch from Peter Petrov.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-28 17:05:49 +01:00
Marco Costalba
200fc56e9c Fix 'generation' type to uint8_t
When we store this value in TT we cut this to 9 bits,
so we need a smaller variable otherwise comparisons
like:

   replace->generation() == generation

Are always false if generation is bigger then the maximum
TT storable value.

This fixes a very nasty and difficult to spot bug (2 weeks for
regression hunting).

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-28 17:01:01 +01:00
Marco Costalba
d0dc05ad41 Revert sleeping threads
Revert 141caf1d5b + c59efc53c9

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-27 21:26:00 +01:00
Marco Costalba
9ecdfd2401 Revert "Allow split point master to sleep (take 2)"
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-27 21:25:59 +01:00
Marco Costalba
5d6b2f2144 We don't need a stringstream in buildKey()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-23 13:01:11 +01:00
Marco Costalba
efeb37c33f Retire Application class
It is a redundant boiler plate, just call initialization and
resource release directly from main()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-22 18:36:18 +01:00
Marco Costalba
00d9fe8af0 Retire piece.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-22 13:13:48 +01:00
Marco Costalba
85df24624a UCI options names should not be case sensitive
Correctly handle uci option names in a case insensitive way.

Alos fix some indentation while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-21 23:52:51 +01:00
Marco Costalba
f44aea7508 Retire "New Game" UCI option
Was introduced by 403db5a6e9
on 1/12/2009 to correctly handle "loose on time"
LSN filtering functionality, but is now unused.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-21 14:25:30 +01:00
Marco Costalba
fa80479b1d Remove hardcoded 16 from benchmark default positions size
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-21 13:06:53 +01:00
Marco Costalba
df6ba1fa5c Micro-optimize pl_move_is_legal()
This L1/L2 optimization has an incredible +4.7% speedup
in perft test where this function is the most time consumer.

Verified a speed up also in normal bench, although smaller.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-21 12:49:16 +01:00
Marco Costalba
f57d51b7f3 Store "true" and "false" in bool options
UCI protocol uses "true" and "false" for check and button types,
so store that values instead of "1" and "0", this simplifies a
bit the code.

Also a bit strictier option's type checking in debug mode.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-20 18:06:38 +01:00
Marco Costalba
358ccf206b Debug counters don't need to be global
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-20 11:48:04 +01:00
Marco Costalba
24e6ed907b Small touches to engine_name()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-19 23:57:11 +01:00
Marco Costalba
bdfd656c24 Use occupied_squares() in book_key()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-16 19:28:09 +01:00
Marco Costalba
d08a8d76f7 Rearrange pawn moves generation
This patch greatly cleanups generation of pawn moves but
we change the order in which moves are generated so there is
a change in functionality, but not in perft.

The only real functionality change is that now when type == CHECK
we generate knight underpromotion captures only if give check and
not always as before.

Perft is 2% faster and fully verified.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-16 13:43:05 +01:00
Marco Costalba
6f70e762a9 Introduce generate_promotions()
A bit ugly to guarantee no functional change.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-16 13:02:52 +01:00
Marco Costalba
b36900ef44 Simplify generate_pawn_captures()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-15 18:58:15 +01:00
Marco Costalba
a1c02815cc Cleanup Bioskey()
And rename in dataAvailable()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-15 18:58:00 +01:00
Marco Costalba
660378d10e Let bench to have full defaults arguments
Now stockfish bench' defaults to

stockfish bench 128 1 12 default depth

that is the most used line (at least by me)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-09 05:05:08 +01:00
Marco Costalba
9dcc2aad98 Various cleanup in book.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-08 18:51:42 +01:00
Marco Costalba
fad595f5b6 Let benchmark to default to depth 12
And also simplify a lot the code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-08 13:53:44 +01:00
Marco Costalba
d2d953713f Move PieceValue[] and SlidingArray[] where they belong
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-08 13:18:18 +01:00
Marco Costalba
c28b9ef182 Allow split point master to sleep (take 2)
Let to sleep even split point master, it will be waken
up by its slaves when they return from the search.

This time let it be enabled by an UCI option, so
people is free to test it on their Hyper Thread box.

Option is disabled by default.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 23:51:09 +01:00
Marco Costalba
da6e2b5fd1 Use namespace in position.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 13:51:03 +01:00
Marco Costalba
b06f0460a2 Retire uci.h and benchmark.h
Moved the single prototipes where are needed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 13:31:33 +01:00
Marco Costalba
d2ad5acddd Object OpeningBook doen't need to be global
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 13:12:48 +01:00
Marco Costalba
4cd53b68d0 Make rkiss seed deterministic
Search at fixed depth with one thread must be
reproducible so remove randomess from time().

Also better license description.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 12:48:23 +01:00
Marco Costalba
8fb16df70e Let rkiss.h to follow SF coding style
Fix also Makefile after mersenne.cpp has been removed

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 12:22:15 +01:00
Marco Costalba
f5e28ef512 Use Heinz's RKiss instead of marsenne
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 11:52:59 +01:00
Marco Costalba
287556f97d Fix an off by one bug in print_uci_options()
Last option was not printed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 10:53:31 +01:00
Marco Costalba
469e7c5143 Retire bitbase.h
Moved the only prototipe where is needed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-07 10:53:19 +01:00
Marco Costalba
bacb645939 Rewrite options handling in an object oriented fashion
Big rewrite and about 100 lines removed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-05 06:10:05 +01:00
Marco Costalba
fb50e16cdd Retire push_button() and button_was_pressed()
Directly access the underlying bool option.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-03 13:19:55 +01:00
Marco Costalba
9f626725ae Prefer int to uint8_t when possible
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-01 13:17:01 +01:00
Marco Costalba
cfca92cd7c Add "mingw" compiler to Makefile
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-01 12:17:54 +01:00
Marco Costalba
d607febb38 Fix MinGW warnings
I finally got SF to compile under MinGW (after adding pthread libraries)
and here are the fixed warnings.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-11-01 11:44:46 +01:00
Marco Costalba
19cf779629 Allocate RootPosition on the stack
And pass it as an argument.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-31 11:53:49 +01:00
Marco Costalba
d74025a34e Update nodes after a do_move()
And also store the node counter in Position and not in Thread.
This will allow to properly count nodes also in sub trees with
SMP active.

This requires a surprisingly high number of changes
in a lot of places to make it work properly.

No functional change but node count changed for obvious reasons.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-31 11:22:40 +01:00
Marco Costalba
49a6fee4fa Fix some icc's "statement is unreachable" warnings
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-30 19:19:40 +01:00
Marco Costalba
2991ff0dc2 Move moveCount update near the SpNode case
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-30 18:26:41 +01:00
Marco Costalba
c416133e2f Introduce and use TranspositionTable::refresh()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-30 15:22:10 +01:00
Marco Costalba
ff95bbd41f Use margins[] array in evaluate
It will be used by future patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-30 14:24:19 +01:00
Marco Costalba
2d7a417d0a More readable search/qsearch dispatch
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-30 12:32:38 +01:00
Marco Costalba
f790752daa Fix last leak detected by Valgrind
This was subtle and google was my friend.

The leak was in _dl_allocate_tls called by pthread_create() and
is due to the fact that threads are created in joinable state so that
once terminated are not freed. To make the thread to release
its resources upon termination we should set them in detached state.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-24 09:57:30 +01:00
Marco Costalba
5254ca22f3 Fix a memcpy() warning under Valgrind
Fix warning: "Source and destination overlap in memcpy"

This happens when we call multiple time do_move() with the
same state, for instance when we don't need to undo the move.

This is what valgrind docs say:

You don't want the two blocks to overlap because one of them could
get partially overwritten by the copying.

You might think that Memcheck is being overly pedantic reporting this
in the case where 'dst' is less than 'src'. For example, the obvious way
to implement memcpy() is by copying from the first byte to the last.
However, the optimisation guides of some architectures recommend copying
from the last byte down to the first. Also, some implementations of
memcpy() zero 'dst' before copying, because zeroing the destination's
cache line(s) can improve performance.

In addition, for many of these functions, the POSIX standards have wording
along the lines "If copying takes place between objects that overlap,
the behavior is undefined." Hence overlapping copies violate the standard.

The moral of the story is: if you want to write truly portable code, don't
make any assumptions about the language implementation.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-24 09:56:57 +01:00
Marco Costalba
5b445cdf59 Revert previous patch
It seems we have a speed regression under Linux, anyhow
commit and revert to leave some documentation in case we
want to try again in the future.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-24 09:53:19 +01:00
Marco Costalba
96e589646d Allow split point master to sleep
Let to sleep even split point master, it will be waken up
by its slaves when they return from the search.

With this patch we get maximum HT speedup

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-23 13:50:54 +01:00
Marco Costalba
c81bf3743f Re-add "Pass evalMargin through SearchStack as eval"
It has more sense to treat the two evaluation metrics
in the same way.

As a side effect now we use the correct eval margin when
pruning in a SplitPoint node.

No functional change in single thread.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-23 08:07:26 +01:00
Marco Costalba
f6e11ee2a3 Finally retire sp_search()
Fix the movcount updating bug and let search() to completely
subsititute sp_search().

No functional change even with fakes split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-23 07:51:35 +01:00
Marco Costalba
65606bc49e Temporary restore old sp_search()
There is a bug in the conversion that is triggered when testing
with faked split and that I missed somehow :-(

To allow proper testing on cluster restore old sp_search()
until I don't fiugre up what's happened.

Restored to be functional equivalent to old behaviour both in
single thread and in faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-18 08:34:25 +01:00
Marco Costalba
3b7bf34b02 Revert "Pass evalMargin through SearchStack as eval"
Restore full no functional change also in Faked Split mode.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-18 08:19:48 +01:00
Marco Costalba
141caf1d5b Don't wake up /sleep threads in think() anymore
When entering and exiting from think() we don't need any special
wake up / sleeping code because we want available threads to keep
sleeping.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 13:12:58 +01:00
Marco Costalba
c59efc53c9 Enable sleeping of available threads
This simple patch has devastating consequences ;-)

Now an available thread goes to sleep and is waked up after
being allocated.

This patch allows Stockfish to dramatically increase performances
on HyperThreading systems.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 11:38:00 +01:00
Marco Costalba
8fdc635255 Use fast SRWLOCK locks under Windows
They are fast and also have the same semantic of Linux ones.

This allow to simplify the code and especially to use
SleepConditionVariableSRW() to wait on a condition releaseing the lock,
this has the same semantic as pthread_cond_wait().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 11:04:52 +01:00
Marco Costalba
472971f851 Remove some ifdef from wake_sleeping_thread()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 09:35:44 +01:00
Marco Costalba
389edb8099 Retire put_threads_to_sleep()
Obsoleted by previous patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 09:03:39 +01:00
Marco Costalba
13d8231746 Retire THREAD_SLEEPING and use THREAD_AVAILABLE instead
This is a prerequisite for future work and anyhow removes
a state flag, so it is good anyhow.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 08:59:07 +01:00
Marco Costalba
9440fb06da Retire AllThreadsShouldSleep flag
It is redundant and complicates the already complicated
SMP code for no reason.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 08:39:03 +01:00
Marco Costalba
3a564ed5db Destroy wait conditions before exiting
We already do this for locks. Also rename SitIdleEvent
in WaitCond to be uniform with Lunix naming.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 07:36:14 +01:00
Marco Costalba
1fdb436e78 Change thread API to use one wait condition per thread
This is the native way done in Windows and we will use it
for future work, so change Linux to do the same.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-17 08:00:42 +01:00
Marco Costalba
dcf2edfdea Do not shadow SplitPoint struct with search() parameter
Also retire move_is_killer() is called only from one place.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 15:39:07 +01:00
Marco Costalba
85a7456bd7 Fixed some warnings when using -Weffc++ gcc option
Plus some other icc warnings popped up with new and strictier
compile options.

No functional and speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 15:00:20 +01:00
Marco Costalba
d664773a83 Fix a shadowed variable warning under icc
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 15:00:07 +01:00
Marco Costalba
f092667460 Retire now obsoleted do_sp_search() trampoline code
We can call search() directly from idle_loop()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 12:20:16 +01:00
Marco Costalba
19ff8e2902 Pass evalMargin through SearchStack as eval
It has more sense to treat the two evaluation metrics
in the same way.

As a side effect now we use the correct eval margin when
pruning in a SplitPoint node.

No functional change in single thread.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 11:54:44 +01:00
Marco Costalba
a7f4ee7540 Unify sp_search() and search() step 3
Remove old sp_search() code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 11:40:49 +01:00
Marco Costalba
f7722d4de7 Unify sp_search() and search() step 2
Modify search() to be able to handle split points

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 11:20:43 +01:00
Marco Costalba
37055ad002 Unify sp_search() and search() step 1
Rewrite sp_search() to have same signature of search()

This is the first prerequistite step toward unification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-16 09:49:45 +01:00
Marco Costalba
79a7647fe0 Pass moveCount by value in split()
Actually it is an error to update back moveCount value after split()
because it is used in update_history() to access movesSearched[]
array. But becasue this vector is not updated in the split point
we end up with an access of stale data.

Bug has been hidden til now because we 'forgot' to update
moveCount before returning from split().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-12 12:33:44 +01:00
Marco Costalba
00950fec00 Sync sp_search() with search()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-12 12:19:47 +01:00
Marco Costalba
7c7a77698a Better document some threads functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-12 12:11:20 +01:00
Marco Costalba
083ed1ce94 Document an assert in idle_loop()
Thanks to Bruno Causse for the clarification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-11 19:56:35 +01:00
Marco Costalba
2feeb206ff Use VALUE_DRAW instead of VALUE_ZERO where better
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-10 09:05:46 +01:00
Marco Costalba
5dfbbb79be Use do_move_bb() in move_attacks_square()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-10 08:32:12 +01:00
Marco Costalba
d440ddb487 Another cleanup in evaluate_pawns()
Suggested by Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-10 08:32:03 +01:00
Marco Costalba
9c9914d72a Micro optimize open files calculation
Committed mostly because is also a cleanup...

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-09 15:22:00 +01:00
Marco Costalba
a0474a72a6 Rearrange pawn penalities arrays
A clean up that is also a prerequisite for next patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-09 13:43:43 +01:00
Marco Costalba
7733dadfd7 Small codestyle touches
Mostly suggested by Justin (UncombedCoconut), the 0ULL -> 0 conversion
is mine.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-09 13:05:58 +01:00
Marco Costalba
9ca4359f36 Properly set to zero stuff returned by 'new'
Language guarantees that c'tor is called, but without any c'tor
it happens to work by accident because OS zeroes out the freshly
allocated pages. The problem is that if I deallocate and allocate
again, the second time pages are no more newly come by the OS and
so could contain stale info.

A practical case could be if we change TT size or numbers of
threads on the fly while already running.

Bug spotted by Justin Blanchard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-07 03:57:33 +01:00
Marco Costalba
f00c976bb2 Retire updateKingTables[]
Suggested by Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-06 19:28:27 +01:00
Marco Costalba
1bbbc13b46 Skip ei.kingZone[] initialization together with king safety
Another microptimization by Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-05 22:32:53 +01:00
Marco Costalba
1ee1d852fe Skip an useless compare in space evaluation
Spotted by Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-05 22:16:09 +01:00
Marco Costalba
812e843939 Restore development version
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-05 19:40:49 +01:00
54 changed files with 2208 additions and 2901 deletions

View File

@@ -33,10 +33,9 @@ BINDIR = $(PREFIX)/bin
PGOBENCH = ./$(EXE) bench 32 1 10 default depth PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files ### Object files
OBJS = application.o bitboard.o pawns.o material.o endgame.o evaluate.o main.o \ OBJS = bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
misc.o move.o movegen.o history.o movepick.o search.o piece.o \ misc.o move.o movegen.o history.o movepick.o search.o position.o \
position.o direction.o tt.o uci.o ucioption.o \ tt.o uci.o ucioption.o book.o bitbase.o san.o benchmark.o timeman.o
mersenne.o book.o bitbase.o san.o benchmark.o timeman.o
### ========================================================================== ### ==========================================================================
@@ -200,6 +199,15 @@ ifeq ($(COMP),)
COMP=gcc COMP=gcc
endif endif
ifeq ($(COMP),mingw)
comp=mingw
CXX=g++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
ifeq ($(COMP),gcc) ifeq ($(COMP),gcc)
comp=gcc comp=gcc
CXX=g++ CXX=g++
@@ -219,14 +227,18 @@ ifeq ($(COMP),icc)
endif endif
### 3.2 General compiler settings ### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -ansi -fno-exceptions -fno-rtti $(EXTRACXXFLAGS) CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc) ifeq ($(comp),gcc)
CXXFLAGS += -pedantic -Wno-long-long -Wextra CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra
endif
ifeq ($(comp),mingw)
CXXFLAGS += -Wno-long-long -Wextra
endif endif
ifeq ($(comp),icc) ifeq ($(comp),icc)
CXXFLAGS += -wd383,869,981,10187,10188,11505,11503 CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif endif
ifeq ($(os),osx) ifeq ($(os),osx)
@@ -261,6 +273,10 @@ ifeq ($(optimize),yes)
endif endif
endif endif
ifeq ($(comp),mingw)
CXXFLAGS += -O3
endif
ifeq ($(comp),icc) ifeq ($(comp),icc)
CXXFLAGS += -fast CXXFLAGS += -fast
@@ -340,6 +356,7 @@ help:
@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 "" @echo ""
@echo "Non-standard targets:" @echo "Non-standard targets:"
@echo "" @echo ""
@@ -412,7 +429,7 @@ install:
-strip $(BINDIR)/$(EXE) -strip $(BINDIR)/$(EXE)
clean: clean:
$(RM) $(EXE) *.o .depend *~ core bench.txt *.gcda $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
testrun: testrun:
@$(PGOBENCH) @$(PGOBENCH)
@@ -453,7 +470,7 @@ config-sanity:
@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)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw"
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS) $(CXX) -o $@ $(OBJS) $(LDFLAGS)

View File

@@ -1,78 +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/>.
*/
////
//// Includes
////
#include "bitboard.h"
#include "direction.h"
#include "endgame.h"
#include "evaluate.h"
#include "material.h"
#include "mersenne.h"
#include "misc.h"
#include "movepick.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
/// Application class is in charge of initializing global resources
/// at startup and cleanly releases them when program terminates.
Application::Application() {
init_mersenne();
init_direction_table();
init_bitboards();
init_uci_options();
Position::init_zobrist();
Position::init_piece_square_tables();
init_eval(1);
init_bitbases();
init_search();
init_threads();
// Make random number generation less deterministic, for book moves
for (int i = abs(get_system_time() % 10000); i > 0; i--)
genrand_int32();
}
void Application::initialize() {
// A static Application object is allocated
// once only when this function is called.
static Application singleton;
}
void Application::free_resources() {
// Warning, following functions reference global objects that
// must be still alive when free_resources() is called.
exit_threads();
quit_eval();
}
void Application::exit_with_failure() {
exit(EXIT_FAILURE);
}

View File

@@ -1,39 +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(APPLICATION_H_INCLUDED)
#define APPLICATION_H_INCLUDED
/// Singleton class used to housekeep memory and global resources
/// so to be sure we always leave in a clean state.
class Application {
Application();
Application(const Application&);
public:
static void initialize();
static void free_resources();
static void exit_with_failure();
};
#endif // !defined(APPLICATION_H_INCLUDED)

View File

@@ -22,10 +22,8 @@
//// Includes //// Includes
//// ////
#include <fstream> #include <fstream>
#include <sstream>
#include <vector> #include <vector>
#include "benchmark.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
@@ -36,7 +34,7 @@ using namespace std;
//// Variables //// Variables
//// ////
const string BenchmarkPositions[] = { static const string BenchmarkPositions[] = {
"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 -",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
@@ -52,7 +50,8 @@ const string BenchmarkPositions[] = {
"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",
""
}; };
@@ -61,63 +60,49 @@ const string BenchmarkPositions[] = {
//// ////
/// 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 time each. There are four 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 time in seconds spent for each position (optional, default /// be used, the limit value spent for each position (optional, default
/// is 60) and an optional file name where to look for positions in fen /// is ply 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above). /// format (default are the BenchmarkPositions defined above) and the type
/// of the limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt. /// The analysis is written to a file named bench.txt.
void benchmark(const string& commandLine) { void benchmark(int argc, char* argv[]) {
istringstream csVal(commandLine); vector<string> positions;
istringstream csStr(commandLine); string ttSize, threads, valStr, posFile, valType;
string ttSize, threads, fileName, limitType, timFile;
int val, secsPerPos, maxDepth, maxNodes; int val, secsPerPos, maxDepth, maxNodes;
csStr >> ttSize; ttSize = argc > 2 ? argv[2] : "128";
csVal >> val; threads = argc > 3 ? argv[3] : "1";
if (val < 4 || val > 1024) valStr = argc > 4 ? argv[4] : "12";
{ posFile = argc > 5 ? argv[5] : "default";
cerr << "The hash table size must be between 4 and 1024" << endl; valType = argc > 6 ? argv[6] : "depth";
Application::exit_with_failure();
}
csStr >> threads;
csVal >> val;
if (val < 1 || val > MAX_THREADS)
{
cerr << "The number of threads must be between 1 and " << MAX_THREADS << endl;
Application::exit_with_failure();
}
set_option_value("Hash", ttSize);
set_option_value("Threads", threads);
set_option_value("OwnBook", "false");
set_option_value("Use Search Log", "true");
set_option_value("Search Log Filename", "bench.txt");
csVal >> val; Options["Hash"].set_value(ttSize);
csVal >> fileName; Options["Threads"].set_value(threads);
csVal >> limitType; Options["OwnBook"].set_value("false");
csVal >> timFile; Options["Use Search Log"].set_value("true");
Options["Search Log Filename"].set_value("bench.txt");
secsPerPos = maxDepth = maxNodes = 0; secsPerPos = maxDepth = maxNodes = 0;
val = atoi(valStr.c_str());
if (limitType == "time") if (valType == "depth" || valType == "perft")
secsPerPos = val * 1000;
else if (limitType == "depth" || limitType == "perft")
maxDepth = val; maxDepth = val;
else if (valType == "time")
secsPerPos = val * 1000;
else else
maxNodes = val; maxNodes = val;
vector<string> positions; if (posFile != "default")
if (fileName != "default")
{ {
ifstream fenFile(fileName.c_str()); ifstream fenFile(posFile.c_str());
if (!fenFile.is_open()) if (!fenFile.is_open())
{ {
cerr << "Unable to open positions file " << fileName << endl; cerr << "Unable to open positions file " << posFile << endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
string pos; string pos;
while (fenFile.good()) while (fenFile.good())
@@ -128,19 +113,8 @@ void benchmark(const string& commandLine) {
} }
fenFile.close(); fenFile.close();
} else } else
for (int i = 0; i < 16; i++) for (int i = 0; !BenchmarkPositions[i].empty(); i++)
positions.push_back(string(BenchmarkPositions[i])); positions.push_back(BenchmarkPositions[i]);
ofstream timingFile;
if (!timFile.empty())
{
timingFile.open(timFile.c_str(), ios::out | ios::app);
if (!timingFile.is_open())
{
cerr << "Unable to open timing file " << timFile << endl;
Application::exit_with_failure();
}
}
vector<string>::iterator it; vector<string>::iterator it;
int cnt = 1; int cnt = 1;
@@ -149,11 +123,11 @@ void benchmark(const string& commandLine) {
for (it = positions.begin(); it != positions.end(); ++it, ++cnt) for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
{ {
Move moves[1] = {MOVE_NONE}; Move moves[1] = { MOVE_NONE };
int dummy[2] = {0, 0}; int dummy[2] = { 0, 0 };
Position pos(*it, 0); Position pos(*it, 0);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl; cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (limitType == "perft") if (valType == "perft")
{ {
int64_t perftCnt = perft(pos, maxDepth * ONE_PLY); int64_t perftCnt = perft(pos, maxDepth * ONE_PLY);
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl; cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
@@ -161,7 +135,7 @@ void benchmark(const string& commandLine) {
} else { } else {
if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves)) if (!think(pos, false, false, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
break; break;
totalNodes += nodes_searched(); totalNodes += pos.nodes_searched();
} }
} }
@@ -171,16 +145,10 @@ void benchmark(const string& commandLine) {
<< "\nNodes searched : " << totalNodes << "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl; << "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
if (!timFile.empty())
{
timingFile << cnt << endl << endl;
timingFile.close();
}
// Under MS Visual C++ debug window always unconditionally closes // Under MS Visual C++ debug window always unconditionally closes
// when program exits, this is bad because we want to read results before. // when program exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64)) #if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl; cerr << "Press any key to exit" << endl;
cin >> fileName; cin >> ttSize;
#endif #endif
} }

View File

@@ -1,37 +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(BENCHMARK_H_INCLUDED)
#define BENCHMARK_H_INCLUDED
////
//// Includes
////
#include <string>
////
//// Prototypes
////
extern void benchmark(const std::string& commandLine);
#endif // !defined(BENCHMARK_H_INCLUDED)

View File

@@ -24,7 +24,6 @@
#include <cassert> #include <cassert>
#include "bitbase.h"
#include "bitboard.h" #include "bitboard.h"
#include "square.h" #include "square.h"

View File

@@ -1,38 +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(BITBASE_H_INCLUDED)
#define BITBASE_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Prototypes
////
extern void generate_kpk_bitbase(uint8_t bitbase[]);
#endif // !defined(BITBASE_H_INCLUDED)

View File

@@ -26,7 +26,6 @@
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "direction.h"
#if defined(IS_64BIT) #if defined(IS_64BIT)
@@ -226,7 +225,6 @@ Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65]; Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64]; Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64]; Bitboard SquaresInFrontMask[2][64];
@@ -247,10 +245,10 @@ uint8_t BitCount8Bit[256];
namespace { namespace {
void init_masks(); void init_masks();
void init_ray_bitboards();
void init_attacks(); void init_attacks();
void init_between_bitboards(); void init_between_bitboards();
void init_pseudo_attacks(); void init_pseudo_attacks();
SquareDelta squares_delta(Square orig, Square dest);
Bitboard index_to_bitboard(int index, Bitboard mask); Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2], Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin, int fmax, int rmin, int rmax); int fmin, int fmax, int rmin, int rmax);
@@ -289,7 +287,6 @@ void init_bitboards() {
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}}; int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks(); init_masks();
init_ray_bitboards();
init_attacks(); init_attacks();
init_between_bitboards(); init_between_bitboards();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas); init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
@@ -403,25 +400,10 @@ namespace {
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s); AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
} }
for (Bitboard b = 0ULL; b < 256ULL; b++) for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b); BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
} }
int remove_bit_8(int i) { return ((i & ~15) >> 1) | (i & 7); }
void init_ray_bitboards() {
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
for (int i = 0; i < 128; i = (i + 9) & ~8)
for (int j = 0; j < 8; j++)
{
RayBB[remove_bit_8(i)][j] = EmptyBoardBB;
for (int k = i + d[j]; (k & 0x88) == 0; k += d[j])
set_bit(&(RayBB[remove_bit_8(i)][j]), Square(remove_bit_8(k)));
}
}
void init_attacks() { void init_attacks() {
const int step[16][8] = { const int step[16][8] = {
@@ -472,22 +454,40 @@ namespace {
return result; return result;
} }
SquareDelta squares_delta(Square orig, Square dest) {
const SquareDelta deltas[] = { DELTA_N, DELTA_NE, DELTA_E, DELTA_SE,
DELTA_S, DELTA_SW, DELTA_W, DELTA_NW };
for (int idx = 0; idx < 8; idx++)
{
Square s = orig + deltas[idx];
while (square_is_ok(s) && square_distance(s, s - deltas[idx]) == 1)
{
if (s == dest)
return deltas[idx];
s += deltas[idx];
}
}
return DELTA_NONE;
}
void init_between_bitboards() { void init_between_bitboards() {
const SquareDelta step[8] = { DELTA_E, DELTA_W, DELTA_N, DELTA_S, Square s1, s2, s3;
DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE }; SquareDelta d;
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
{ {
BetweenBB[s1][s2] = EmptyBoardBB; BetweenBB[s1][s2] = EmptyBoardBB;
SignedDirection d = signed_direction_between_squares(s1, s2); d = squares_delta(s1, s2);
if (d != SIGNED_DIR_NONE) if (d != DELTA_NONE)
{ for (s3 = s1 + d; s3 != s2; s3 += d)
for (Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
set_bit(&(BetweenBB[s1][s2]), s3); set_bit(&(BetweenBB[s1][s2]), s3);
}
} }
} }
@@ -511,7 +511,7 @@ namespace {
for (int i = 0, index = 0; i < 64; i++) for (int i = 0, index = 0; i < 64; i++)
{ {
attackIndex[i] = index; attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6); mask[i] = sliding_attacks(i, 0, 4, deltas, 1, 6, 1, 6);
#if defined(IS_64BIT) #if defined(IS_64BIT)
int j = (1 << (64 - shift[i])); int j = (1 << (64 - shift[i]));

View File

@@ -26,7 +26,6 @@
//// Includes //// Includes
//// ////
#include "direction.h"
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
#include "types.h" #include "types.h"
@@ -68,7 +67,6 @@ extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65]; extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttackBB[16][64]; extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64]; extern Bitboard SquaresInFrontMask[2][64];
@@ -209,14 +207,6 @@ inline Bitboard behind_bb(Color c, Square s) {
} }
/// ray_bb() gives a bitboard representing all squares along the ray in a
/// given direction from a given square.
inline Bitboard ray_bb(Square s, SignedDirection d) {
return RayBB[s][d];
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(), /// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a /// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing /// bitboard of occupied squares as input, and return a bitboard representing
@@ -307,6 +297,15 @@ inline Bitboard attack_span_mask(Color c, Square s) {
} }
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3));
}
/// first_1() finds the least significant nonzero bit in a nonzero bitboard. /// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a /// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard. /// nonzero bitboard.

View File

@@ -32,30 +32,20 @@
#include <cassert> #include <cassert>
#include "book.h" #include "book.h"
#include "mersenne.h"
#include "movegen.h" #include "movegen.h"
using namespace std; using namespace std;
////
//// Global variables
////
Book OpeningBook;
//// ////
//// Local definitions //// Local definitions
//// ////
namespace { namespace {
/// Book entry size in bytes // Book entry size in bytes
const int EntrySize = 16; const int EntrySize = 16;
// 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 uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
@@ -320,17 +310,13 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Indices to the Random64[] array
const int PieceIdx = 0;
const int CastleIdx = 768;
const int EnPassantIdx = 772;
const int TurnIdx = 780;
/// Indices to the Random64[] array // Local functions
const int RandomPiece = 0;
const int RandomCastle = 768;
const int RandomEnPassant = 772;
const int RandomTurn = 780;
/// Prototypes
uint64_t book_key(const Position& pos); uint64_t book_key(const Position& pos);
uint64_t book_piece_key(Piece p, Square s); uint64_t book_piece_key(Piece p, Square s);
uint64_t book_castle_key(const Position& pos); uint64_t book_castle_key(const Position& pos);
@@ -343,6 +329,13 @@ namespace {
//// Functions //// Functions
//// ////
// C'tor. Make random number generation less deterministic, for book moves
Book::Book() {
for (int i = abs(get_system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>();
}
/// Destructor. Be sure file is closed before we leave. /// Destructor. Be sure file is closed before we leave.
@@ -352,6 +345,16 @@ Book::~Book() {
} }
/// 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 (is_open())
ifstream::close();
}
/// Book::open() opens a book file with a given file name /// Book::open() opens a book file with a given file name
void Book::open(const string& fName) { void Book::open(const string& fName) {
@@ -361,6 +364,8 @@ void Book::open(const string& fName) {
fileName = fName; fileName = fName;
ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary); ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary);
// Silently return when asked to open a non-exsistent file
if (!is_open()) if (!is_open())
return; return;
@@ -372,21 +377,11 @@ void Book::open(const string& fName) {
if (!good()) if (!good())
{ {
cerr << "Failed to open book file " << fileName << endl; cerr << "Failed to open book file " << fileName << endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
} }
/// 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 (is_open())
ifstream::close();
}
/// Book::file_name() returns the file name of the currently active book, /// Book::file_name() returns the file name of the currently active book,
/// or the empty string if no book is open. /// or the empty string if no book is open.
@@ -406,7 +401,7 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
BookEntry entry; BookEntry entry;
int bookMove = MOVE_NONE; int bookMove = MOVE_NONE;
int scoresSum = 0, bestScore = 0; unsigned scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos); uint64_t key = book_key(pos);
// Choose a book move among the possible moves for the given position // Choose a book move among the possible moves for the given position
@@ -416,9 +411,7 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
if (entry.key != key) if (entry.key != key)
break; break;
int score = entry.count; unsigned score = entry.count;
assert(score > 0);
// If findBestMove is true choose highest rated book move // If findBestMove is true choose highest rated book move
if (findBestMove) if (findBestMove)
@@ -435,13 +428,14 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
// high score it has more probability to be choosen then a one with // high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen. // lower score. Note that first entry is always chosen.
scoresSum += score; scoresSum += score;
if (int(genrand_int32() % scoresSum) < score) if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move; bookMove = entry.move;
} }
if (!bookMove) if (!bookMove)
return MOVE_NONE; return MOVE_NONE;
MoveStack mlist[256]; // Verify the book move is legal
MoveStack mlist[MOVES_MAX];
MoveStack* last = generate_moves(pos, mlist); MoveStack* last = generate_moves(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++) for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & 07777) == bookMove) if ((int(cur->move) & 07777) == bookMove)
@@ -474,6 +468,7 @@ int Book::find_key(uint64_t key) {
assert(mid >= left && mid < right); assert(mid >= left && mid < right);
read_entry(entry, mid); read_entry(entry, mid);
if (key <= entry.key) if (key <= entry.key)
right = mid; right = mid;
else else
@@ -483,7 +478,7 @@ int Book::find_key(uint64_t key) {
assert(left == right); assert(left == right);
read_entry(entry, left); read_entry(entry, left);
return (entry.key == key)? left : bookSize; return entry.key == key ? left : bookSize;
} }
@@ -497,11 +492,13 @@ void Book::read_entry(BookEntry& entry, int idx) {
assert(is_open()); assert(is_open());
seekg(idx * EntrySize, ios_base::beg); seekg(idx * EntrySize, ios_base::beg);
*this >> entry; *this >> entry;
if (!good()) if (!good())
{ {
cerr << "Failed to read book entry at index " << idx << endl; cerr << "Failed to read book entry at index " << idx << endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
} }
@@ -512,10 +509,11 @@ void Book::read_entry(BookEntry& entry, int idx) {
uint64_t Book::read_integer(int size) { uint64_t Book::read_integer(int size) {
char buf[8]; char buf[8];
uint64_t n = 0;
read(buf, size); read(buf, size);
// Numbers are stored on disk as a binary byte stream // Numbers are stored on disk as a binary byte stream
uint64_t n = 0ULL;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
n = (n << 8) + (unsigned char)buf[i]; n = (n << 8) + (unsigned char)buf[i];
@@ -531,23 +529,15 @@ namespace {
uint64_t book_key(const Position& pos) { uint64_t book_key(const Position& pos) {
uint64_t result = 0ULL; uint64_t result = 0;
Bitboard b = pos.occupied_squares();
for (Color c = WHITE; c <= BLACK; c++) while (b)
{ {
Bitboard b = pos.pieces_of_color(c); Square s = pop_1st_bit(&b);
result ^= book_piece_key(pos.piece_on(s), s);
while (b)
{
Square s = pop_1st_bit(&b);
Piece p = pos.piece_on(s);
assert(piece_is_ok(p));
assert(color_of_piece(p) == c);
result ^= book_piece_key(p, s);
}
} }
result ^= book_castle_key(pos); result ^= book_castle_key(pos);
result ^= book_ep_key(pos); result ^= book_ep_key(pos);
result ^= book_color_key(pos); result ^= book_color_key(pos);
@@ -557,39 +547,41 @@ namespace {
uint64_t book_piece_key(Piece p, Square s) { uint64_t book_piece_key(Piece p, Square s) {
/// Convert pieces to the range 0..11 // Convert pieces to the range 0..11
static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 }; static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)]; return Random64[PieceIdx + (PieceTo12[int(p)]^1) * 64 + int(s)];
} }
uint64_t book_castle_key(const Position& pos) { uint64_t book_castle_key(const Position& pos) {
uint64_t result = 0ULL; uint64_t result = 0;
if (pos.can_castle_kingside(WHITE)) if (pos.can_castle_kingside(WHITE))
result ^= Random64[RandomCastle+0]; result ^= Random64[CastleIdx + 0];
if (pos.can_castle_queenside(WHITE)) if (pos.can_castle_queenside(WHITE))
result ^= Random64[RandomCastle+1]; result ^= Random64[CastleIdx + 1];
if (pos.can_castle_kingside(BLACK)) if (pos.can_castle_kingside(BLACK))
result ^= Random64[RandomCastle+2]; result ^= Random64[CastleIdx + 2];
if (pos.can_castle_queenside(BLACK)) if (pos.can_castle_queenside(BLACK))
result ^= Random64[RandomCastle+3]; result ^= Random64[CastleIdx + 3];
return result; return result;
} }
uint64_t book_ep_key(const Position& pos) { uint64_t book_ep_key(const Position& pos) {
return (pos.ep_square() == SQ_NONE ? 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())]);
return pos.ep_square() == SQ_NONE ? 0 : Random64[EnPassantIdx + square_file(pos.ep_square())];
} }
uint64_t book_color_key(const Position& pos) { uint64_t book_color_key(const Position& pos) {
return (pos.side_to_move() == WHITE ? Random64[RandomTurn] : 0ULL);
return pos.side_to_move() == WHITE ? Random64[TurnIdx] : 0;
} }
} }

View File

@@ -38,6 +38,7 @@
#include "move.h" #include "move.h"
#include "position.h" #include "position.h"
#include "rkiss.h"
//// ////
@@ -56,7 +57,7 @@ class Book : private std::ifstream {
Book(const Book&); // just decleared.. Book(const Book&); // just decleared..
Book& operator=(const Book&); // ..to avoid a warning Book& operator=(const Book&); // ..to avoid a warning
public: public:
Book() {} Book();
~Book(); ~Book();
void open(const std::string& fName); void open(const std::string& fName);
void close(); void close();
@@ -74,14 +75,7 @@ private:
std::string fileName; std::string fileName;
int bookSize; int bookSize;
RKISS RKiss;
}; };
////
//// Global variables
////
extern Book OpeningBook;
#endif // !defined(BOOK_H_INCLUDED) #endif // !defined(BOOK_H_INCLUDED)

View File

@@ -31,7 +31,10 @@ enum Depth {
ONE_PLY = 2, ONE_PLY = 2,
DEPTH_ZERO = 0, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };

View File

@@ -1,87 +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/>.
*/
////
//// Includes
////
#include "direction.h"
#include "square.h"
////
//// Local definitions
////
namespace {
const SquareDelta directionToDelta[] = {
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
};
bool reachable(Square orig, Square dest, SignedDirection dir) {
SquareDelta delta = directionToDelta[dir];
Square from = orig;
Square to = from + delta;
while (to != dest && square_distance(to, from) == 1 && square_is_ok(to))
{
from = to;
to += delta;
}
return (to == dest && square_distance(from, to) == 1);
}
}
////
//// Variables
////
uint8_t DirectionTable[64][64];
uint8_t SignedDirectionTable[64][64];
////
//// Functions
////
void init_direction_table() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
DirectionTable[s1][s2] = uint8_t(DIR_NONE);
SignedDirectionTable[s1][s2] = uint8_t(SIGNED_DIR_NONE);
if (s1 == s2)
continue;
for (SignedDirection d = SIGNED_DIR_E; d != SIGNED_DIR_NONE; d++)
{
if (reachable(s1, s2, d))
{
SignedDirectionTable[s1][s2] = uint8_t(d);
DirectionTable[s1][s2] = uint8_t(d / 2);
break;
}
}
}
}

View File

@@ -1,86 +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(DIRECTION_H_INCLUDED)
#define DIRECTION_H_INCLUDED
////
//// Includes
////
#include "square.h"
#include "types.h"
////
//// Types
////
enum Direction {
DIR_E = 0, DIR_N = 1, DIR_NE = 2, DIR_NW = 3, DIR_NONE = 4
};
enum SignedDirection {
SIGNED_DIR_E = 0, SIGNED_DIR_W = 1,
SIGNED_DIR_N = 2, SIGNED_DIR_S = 3,
SIGNED_DIR_NE = 4, SIGNED_DIR_SW = 5,
SIGNED_DIR_NW = 6, SIGNED_DIR_SE = 7,
SIGNED_DIR_NONE = 8
};
ENABLE_OPERATORS_ON(SignedDirection);
////
//// Variables
////
extern uint8_t DirectionTable[64][64];
extern uint8_t SignedDirectionTable[64][64];
////
//// Inline functions
////
inline Direction direction_between_squares(Square s1, Square s2) {
return Direction(DirectionTable[s1][s2]);
}
inline SignedDirection signed_direction_between_squares(Square s1, Square s2) {
return SignedDirection(SignedDirectionTable[s1][s2]);
}
inline int direction_is_diagonal(Square s1, Square s2) {
return DirectionTable[s1][s2] & 2;
}
inline bool direction_is_straight(Square s1, Square s2) {
return DirectionTable[s1][s2] < 2;
}
////
//// Prototypes
////
extern void init_direction_table();
#endif // !defined(DIRECTION_H_INCLUDED)

View File

@@ -24,7 +24,6 @@
#include <cassert> #include <cassert>
#include "bitbase.h"
#include "bitcount.h" #include "bitcount.h"
#include "endgame.h" #include "endgame.h"
@@ -102,6 +101,7 @@ namespace {
/// init_bitbases() is called during program initialization, and simply loads /// init_bitbases() is called during program initialization, and simply loads
/// bitbases from disk into memory. At the moment, there is only the bitbase /// bitbases from disk into memory. At the moment, there is only the bitbase
/// for KP vs K, but we may decide to add other bitbases later. /// for KP vs K, but we may decide to add other bitbases later.
extern void generate_kpk_bitbase(uint8_t bitbase[]);
void init_bitbases() { void init_bitbases() {
generate_kpk_bitbase(KPKBitbase); generate_kpk_bitbase(KPKBitbase);
@@ -367,12 +367,12 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
/// king alone are always draw. /// king alone are always draw.
template<> template<>
Value EvaluationFunction<KmmKm>::apply(const Position&) const { Value EvaluationFunction<KmmKm>::apply(const Position&) const {
return VALUE_ZERO; return VALUE_DRAW;
} }
template<> template<>
Value EvaluationFunction<KNNK>::apply(const Position&) const { Value EvaluationFunction<KNNK>::apply(const Position&) const {
return VALUE_ZERO; return VALUE_DRAW;
} }
/// KBPKScalingFunction scales endgames where the stronger side has king, /// KBPKScalingFunction scales endgames where the stronger side has king,
@@ -699,11 +699,12 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_ZERO;
else else
{ {
Bitboard ray = ray_bb(pawnSq, (strongerSide == WHITE)? SIGNED_DIR_N : SIGNED_DIR_S); Bitboard path = squares_in_front_of(strongerSide, pawnSq);
if (ray & pos.pieces(KING, weakerSide))
if (path & pos.pieces(KING, weakerSide))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_ZERO;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & ray) if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3) && square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_ZERO;
} }

View File

@@ -101,5 +101,4 @@ struct ScalingFunction : public EndgameScalingFunctionBase {
extern void init_bitbases(); extern void init_bitbases();
#endif // !defined(ENDGAME_H_INCLUDED) #endif // !defined(ENDGAME_H_INCLUDED)

View File

@@ -45,11 +45,6 @@ namespace {
// Pointer to pawn hash table entry // Pointer to pawn hash table entry
PawnInfo* pi; PawnInfo* pi;
// updateKingTables[color] is set to true if we have enough material
// to trigger the opponent's king safety calculation. When is false we
// skip the time consuming update of the king attackers tables.
bool updateKingTables[2];
// attackedBy[color][piece type] is a bitboard representing all squares // attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][0] contains // attacked by a given color and piece type, attackedBy[color][0] contains
// all squares attacked by the given color. // all squares attacked by the given color.
@@ -235,7 +230,7 @@ namespace {
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin); Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us> template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei); Score evaluate_threats(const Position& pos, EvalInfo& ei);
@@ -246,7 +241,10 @@ namespace {
template<Color Us> template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei); Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
Score apply_weight(Score v, Score weight); template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight);
Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf); Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf);
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
void init_safety(); void init_safety();
@@ -281,6 +279,7 @@ template<bool HasPopCnt>
Value do_evaluate(const Position& pos, Value& margin) { Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei; EvalInfo ei;
Value margins[2];
Score mobilityWhite, mobilityBlack; Score mobilityWhite, mobilityBlack;
assert(pos.is_ok()); assert(pos.is_ok());
@@ -291,9 +290,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
// in the position object (material + piece square tables). // in the position object (material + piece square tables).
Score bonus = pos.value(); Score bonus = pos.value();
// margin is the uncertainty estimation of position's evaluation // margins[] store the uncertainty estimation of position's evaluation
// and typically is used by the search for pruning decisions. // that typically is used by the search for pruning decisions.
margin = VALUE_ZERO; margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Probe the material hash table // Probe the material hash table
MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos); MaterialInfo* mi = MaterialTable[pos.thread()]->get_material_info(pos);
@@ -302,7 +301,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
// configuration, call it and return. // configuration, call it and return.
if (mi->specialized_eval_exists()) if (mi->specialized_eval_exists())
{
margin = VALUE_ZERO;
return mi->evaluate(pos); return mi->evaluate(pos);
}
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos); ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
@@ -320,8 +322,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Evaluate kings after all other pieces because we need complete attack // Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation. // information when computing the king safety evaluation.
bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margin) bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt>(pos, ei, margin); - evaluate_king<BLACK, HasPopCnt>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
bonus += evaluate_threats<WHITE>(pos, ei) bonus += evaluate_threats<WHITE>(pos, ei)
@@ -331,18 +333,21 @@ Value do_evaluate(const Position& pos, Value& margin) {
bonus += evaluate_passed_pawns<WHITE>(pos, ei) bonus += evaluate_passed_pawns<WHITE>(pos, ei)
- evaluate_passed_pawns<BLACK>(pos, ei); - evaluate_passed_pawns<BLACK>(pos, ei);
Phase phase = mi->game_phase(); // If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
// Evaluate space for both sides, only in middle-game. // Evaluate space for both sides, only in middle-game.
if (phase > PHASE_ENDGAME && mi->space_weight() > 0) if (mi->space_weight())
{ {
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei); int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]); bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]);
} }
// Scale winning side if position is more drawish that what it appears // Scale winning side if position is more drawish that what it appears
ScaleFactor sf = eg_value(bonus) > VALUE_ZERO ? mi->scale_factor(pos, WHITE) ScaleFactor sf = eg_value(bonus) > VALUE_DRAW ? mi->scale_factor(pos, WHITE)
: mi->scale_factor(pos, BLACK); : mi->scale_factor(pos, BLACK);
Phase phase = mi->game_phase();
// If we don't already have an unusual scale factor, check for opposite // If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those. // colored bishop endgames, and use a lower scale for those.
@@ -366,6 +371,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// Interpolate between the middle game and the endgame score // Interpolate between the middle game and the endgame score
margin = margins[pos.side_to_move()];
Value v = scale_by_game_phase(bonus, phase, sf); Value v = scale_by_game_phase(bonus, phase, sf);
return pos.side_to_move() == WHITE ? v : -v; return pos.side_to_move() == WHITE ? v : -v;
} }
@@ -408,7 +414,7 @@ void quit_eval() {
/// read_weights() reads evaluation weights from the corresponding UCI parameters /// read_weights() reads evaluation weights from the corresponding UCI parameters
void read_weights(Color us) { void read_evaluation_uci_options(Color us) {
// King safety is asymmetrical. Our king danger level is weighted by // King safety is asymmetrical. Our king danger level is weighted by
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness". // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
@@ -424,7 +430,7 @@ void read_weights(Color us) {
// If running in analysis mode, make sure we use symmetrical king safety. We do this // If running in analysis mode, make sure we use symmetrical king safety. We do this
// by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average. // by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (get_option_value_bool("UCI_AnalyseMode")) if (Options["UCI_AnalyseMode"].value<bool>())
Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2; Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
init_safety(); init_safety();
@@ -443,15 +449,18 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them)); Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
ei.updateKingTables[Us] = pos.piece_count(Us, QUEEN) && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame;
if (ei.updateKingTables[Us]) // Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
{ {
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : EmptyBoardBB; ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = EmptyBoardBB; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
} } else
ei.kingZone[Us] = ei.kingAttackersCount[Us] = 0;
} }
@@ -515,7 +524,7 @@ namespace {
ei.attackedBy[Us][Piece] |= b; ei.attackedBy[Us][Piece] |= b;
// King attacks // King attacks
if (ei.updateKingTables[Us] && (b & ei.kingZone[Us])) if (b & ei.kingZone[Us])
{ {
ei.kingAttackersCount[Us]++; ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
@@ -653,7 +662,7 @@ namespace {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color // evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value& margin) { Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15; const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -667,8 +676,7 @@ namespace {
// King safety. This is quite complicated, and is almost certainly far // King safety. This is quite complicated, and is almost certainly far
// from optimally tuned. // from optimally tuned.
if ( ei.updateKingTables[Them] if ( ei.kingAttackersCount[Them] >= 2
&& ei.kingAttackersCount[Them] >= 2
&& ei.kingAdjacentZoneAttacksCount[Them]) && ei.kingAdjacentZoneAttacksCount[Them])
{ {
// Find the attacked squares around the king which has no defenders // Find the attacked squares around the king which has no defenders
@@ -755,8 +763,7 @@ namespace {
// be very big, and so capturing a single attacking piece can therefore // be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece. // result in a score change far bigger than the value of the captured piece.
bonus -= KingDangerTable[Us][attackUnits]; bonus -= KingDangerTable[Us][attackUnits];
if (pos.side_to_move() == Us) margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
margin += mg_value(KingDangerTable[Us][attackUnits]);
} }
return bonus; return bonus;
} }
@@ -859,6 +866,168 @@ namespace {
return apply_weight(bonus, Weights[PassedPawns]); return apply_weight(bonus, Weights[PassedPawns]);
} }
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
// Step 1. Hunt for unstoppable pawns. If we find at least one, record how many plies
// are required for promotion
int pliesToGo[2] = {256, 256};
for (Color c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c)))
continue;
Bitboard b = ei.pi->passed_pawns(c);
while (b)
{
Square s = pop_1st_bit(&b);
Square queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
int mtg = RANK_8 - relative_rank(c, s) - int(relative_rank(c, s) == RANK_2);
int oppmtg = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move());
bool pathDefended = ((ei.attackedBy[c][0] & squares_in_front_of(c, s)) == squares_in_front_of(c, s));
if (mtg >= oppmtg && !pathDefended)
continue;
int blockerCount = count_1s<Max15>(squares_in_front_of(c, s) & pos.occupied_squares());
mtg += blockerCount;
if (mtg >= oppmtg && !pathDefended)
continue;
int ptg = 2 * mtg - int(c == pos.side_to_move());
if (ptg < pliesToGo[c])
pliesToGo[c] = ptg;
}
}
// Step 2. If either side cannot promote at least three plies before the other side then
// situation becomes too complex and we give up. Otherwise we determine the possibly "winning side"
if (abs(pliesToGo[WHITE] - pliesToGo[BLACK]) < 3)
return make_score(0, 0);
Color winnerSide = (pliesToGo[WHITE] < pliesToGo[BLACK] ? WHITE : BLACK);
Color loserSide = opposite_color(winnerSide);
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
// We collect the potential candidates in potentialBB.
Bitboard pawnBB = pos.pieces(PAWN, loserSide);
Bitboard potentialBB = pawnBB;
const Bitboard passedBB = ei.pi->passed_pawns(loserSide);
while(pawnBB)
{
Square psq = pop_1st_bit(&pawnBB);
// Check direct advancement
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
// Check if (without even considering any obstacles) we're too far away
if (pliesToGo[winnerSide] + 3 <= ptg)
{
clear_bit(&potentialBB, psq);
continue;
}
// If this is passed pawn, then it _may_ promote in time. We give up.
if (bit_is_set(passedBB, psq))
return make_score(0, 0);
// Doubled pawn is worthless
if (squares_in_front_of(loserSide, psq) & (pos.pieces(PAWN, loserSide)))
{
clear_bit(&potentialBB, psq);
continue;
}
}
// Step 4. Check new passed pawn creation through king capturing and sacrifises
pawnBB = potentialBB;
while(pawnBB)
{
Square psq = pop_1st_bit(&pawnBB);
int mtg = RANK_8 - relative_rank(loserSide, psq) - int(relative_rank(loserSide, psq) == RANK_2);
int ptg = 2 * mtg - int(loserSide == pos.side_to_move());
// Generate list of obstacles
Bitboard obsBB = passed_pawn_mask(loserSide, psq) & pos.pieces(PAWN, winnerSide);
const bool pawnIsOpposed = squares_in_front_of(loserSide, psq) & obsBB;
assert(obsBB);
// How many plies does it take to remove all the obstacles?
int sacptg = 0;
int realObsCount = 0;
int minKingDist = 256;
while(obsBB)
{
Square obSq = pop_1st_bit(&obsBB);
int minMoves = 256;
// Check pawns that can give support to overcome obstacle (Eg. wp: a4,b4 bp: b2. b4 is giving support)
if (!pawnIsOpposed && square_file(psq) != square_file(obSq))
{
Bitboard supBB = in_front_bb(winnerSide, Square(obSq + (winnerSide == WHITE ? 8 : -8)))
& neighboring_files_bb(psq) & potentialBB;
while(supBB) // This while-loop could be replaced with supSq = LSB/MSB(supBB) (depending on color)
{
Square supSq = pop_1st_bit(&supBB);
int dist = square_distance(obSq, supSq);
minMoves = Min(minMoves, dist - 2);
}
}
// Check pawns that can be sacrifised
Bitboard sacBB = passed_pawn_mask(winnerSide, obSq) & neighboring_files_bb(obSq) & potentialBB & ~(1ULL << psq);
while(sacBB) // This while-loop could be replaced with sacSq = LSB/MSB(sacBB) (depending on color)
{
Square sacSq = pop_1st_bit(&sacBB);
int dist = square_distance(obSq, sacSq);
minMoves = Min(minMoves, dist - 2);
}
// If obstacle can be destroyed with immediate pawn sacrifise, it's not real obstacle
if (minMoves <= 0)
continue;
// Pawn sac calculations
sacptg += minMoves * 2;
// King capture calc
realObsCount++;
int kingDist = square_distance(pos.king_square(loserSide), obSq);
minKingDist = Min(minKingDist, kingDist);
}
// Check if pawn sac plan _may_ save the day
if (pliesToGo[winnerSide] + 3 > ptg + sacptg)
return make_score(0, 0);
// Check if king capture plan _may_ save the day (contains some false positives)
int kingptg = (minKingDist + realObsCount) * 2;
if (pliesToGo[winnerSide] + 3 > ptg + kingptg)
return make_score(0, 0);
}
// Step 5. Assign bonus
const int Sign[2] = {1, -1};
return Sign[winnerSide] * make_score(0, (Value) 0x500 - 0x20 * pliesToGo[winnerSide]);
}
// evaluate_space() computes the space evaluation for a given side. The // evaluate_space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares // space evaluation is a simple bonus based on the number of safe squares
@@ -920,8 +1089,8 @@ namespace {
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
// Scale option value from 100 to 256 // Scale option value from 100 to 256
int mg = get_option_value_int(mgOpt) * 256 / 100; int mg = Options[mgOpt].value<int>() * 256 / 100;
int eg = get_option_value_int(egOpt) * 256 / 100; int eg = Options[egOpt].value<int>() * 256 / 100;
return apply_weight(make_score(mg, eg), internalWeight); return apply_weight(make_score(mg, eg), internalWeight);
} }

View File

@@ -29,6 +29,6 @@ class Position;
extern Value evaluate(const Position& pos, Value& margin); extern Value evaluate(const Position& pos, Value& margin);
extern void init_eval(int threads); extern void init_eval(int threads);
extern void quit_eval(); extern void quit_eval();
extern void read_weights(Color sideToMove); extern void read_evaluation_uci_options(Color sideToMove);
#endif // !defined(EVALUATE_H_INCLUDED) #endif // !defined(EVALUATE_H_INCLUDED)

View File

@@ -27,12 +27,16 @@
# include <pthread.h> # include <pthread.h>
typedef pthread_mutex_t Lock; typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL) # define lock_init(x) pthread_mutex_init(x, NULL)
# define lock_grab(x) pthread_mutex_lock(x) # define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x) # define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(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 #else
@@ -41,12 +45,16 @@ typedef pthread_mutex_t Lock;
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
typedef CRITICAL_SECTION Lock; typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
# define lock_init(x) InitializeCriticalSection(x) # define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x) # define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x) # define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(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) #endif // !defined(LOCK_H_INCLUDED)

View File

@@ -28,10 +28,16 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "benchmark.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "endgame.h"
#include "evaluate.h"
#include "material.h"
#include "misc.h" #include "misc.h"
#include "uci.h" #include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND #ifdef USE_CALLGRIND
#include <valgrind/callgrind.h> #include <valgrind/callgrind.h>
@@ -39,19 +45,28 @@
using namespace std; using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void benchmark(int argc, char* argv[]);
//// ////
//// Functions //// Functions
//// ////
int main(int argc, char *argv[]) { int main(int argc, char* argv[]) {
// Disable IO buffering // Disable IO buffering
cout.rdbuf()->pubsetbuf(NULL, 0); cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0); cin.rdbuf()->pubsetbuf(NULL, 0);
// Initialization through global resources manager // Startup initializations
Application::initialize(); init_bitboards();
init_uci_options();
Position::init_zobrist();
Position::init_piece_square_tables();
init_eval(1);
init_bitbases();
init_search();
init_threads();
#ifdef USE_CALLGRIND #ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION; CALLGRIND_START_INSTRUMENTATION;
@@ -66,26 +81,30 @@ int main(int argc, char *argv[]) {
if (CpuHasPOPCNT) if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT." << endl; cout << "Good! CPU has hardware POPCNT." << endl;
// Enter UCI mode // Wait for a command from the user, and passes this command to
uci_main_loop(); // execute_uci_command() and also intercepts EOF from stdin, by
// translating EOF to the "quit" command. This ensures that we
// exit gracefully if the GUI dies unexpectedly.
string cmd;
do {
// Wait for a command from stdin
if (!getline(cin, cmd))
cmd = "quit";
} while (execute_uci_command(cmd));
} }
else // Process command line arguments else // Process command line arguments
{ {
if (string(argv[1]) != "bench" || argc < 4 || argc > 8) if (string(argv[1]) != "bench" || argc > 7)
cout << "Usage: stockfish bench <hash size> <threads> " cout << "Usage: stockfish bench [hash size = 128] [threads = 1] "
<< "[time = 60s] [fen positions file = default] " << "[limit = 12] [fen positions file = default] "
<< "[time, depth, perft or node limited = time] " << "[depth, time, perft or node limited = depth]" << endl;
<< "[timing file name = none]" << endl;
else else
{ benchmark(argc, argv);
string time = argc > 4 ? argv[4] : "60";
string fen = argc > 5 ? argv[5] : "default";
string lim = argc > 6 ? argv[6] : "time";
string tim = argc > 7 ? argv[7] : "";
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim);
}
} }
Application::free_resources(); exit_threads();
quit_eval();
return 0; return 0;
} }

View File

@@ -24,7 +24,6 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <sstream>
#include <map> #include <map>
#include "material.h" #include "material.h"
@@ -48,11 +47,11 @@ namespace {
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][6] = { const int QuadraticCoefficientsSameColor[][8] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } }; { 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
const int QuadraticCoefficientsOppositeColor[][6] = { const int QuadraticCoefficientsOppositeColor[][8] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 }, { 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } }; { 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
@@ -144,8 +143,9 @@ MaterialInfoTable::MaterialInfoTable() {
{ {
cerr << "Failed to allocate " << MaterialTableSize * sizeof(MaterialInfo) cerr << "Failed to allocate " << MaterialTableSize * sizeof(MaterialInfo)
<< " bytes for material hash table." << endl; << " bytes for material hash table." << endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
memset(entries, 0, MaterialTableSize * sizeof(MaterialInfo));
} }
MaterialInfoTable::~MaterialInfoTable() { MaterialInfoTable::~MaterialInfoTable() {
@@ -289,10 +289,12 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
} }
// Evaluate the material balance // Evaluate the material balance
const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), const int pieceCount[2][8] = {
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; { 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) } };
Color c, them; Color c, them;
int sign, pt1, pt2, pc; int sign, pt1, pt2, pc;
int v, vv, matValue = 0; int v, vv, matValue = 0;
@@ -356,7 +358,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
} }
/// EndgameFunctions member definitions. /// EndgameFunctions member definitions
EndgameFunctions::EndgameFunctions() { EndgameFunctions::EndgameFunctions() {
@@ -388,10 +390,10 @@ EndgameFunctions::~EndgameFunctions() {
Key EndgameFunctions::buildKey(const string& keyCode) { Key EndgameFunctions::buildKey(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode[0] == 'K'); assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode.length() < 8); assert(keyCode[0] == 'K');
stringstream s; string fen;
bool upcase = false; bool upcase = false;
// Build up a fen string with the given pieces, note that // Build up a fen string with the given pieces, note that
@@ -401,16 +403,17 @@ Key EndgameFunctions::buildKey(const string& keyCode) {
if (keyCode[i] == 'K') if (keyCode[i] == 'K')
upcase = !upcase; upcase = !upcase;
s << char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i])); fen += char(upcase ? toupper(keyCode[i]) : tolower(keyCode[i]));
} }
s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w - -"; fen += char(8 - keyCode.length() + '0');
return Position(s.str(), 0).get_material_key(); fen += "/8/8/8/8/8/8/8 w - -";
return Position(fen, 0).get_material_key();
} }
const string EndgameFunctions::swapColors(const string& keyCode) { const string EndgameFunctions::swapColors(const string& keyCode) {
// Build corresponding key for the opposite color: "KBPKN" -> "KNKBP" // Build corresponding key for the opposite color: "KBPKN" -> "KNKBP"
size_t idx = keyCode.find("K", 1); size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx); return keyCode.substr(idx) + keyCode.substr(0, idx);
} }

View File

@@ -75,6 +75,9 @@ class EndgameFunctions;
class MaterialInfoTable { class MaterialInfoTable {
MaterialInfoTable(const MaterialInfoTable&);
MaterialInfoTable& operator=(const MaterialInfoTable&);
public: public:
MaterialInfoTable(); MaterialInfoTable();
~MaterialInfoTable(); ~MaterialInfoTable();

View File

@@ -1,171 +0,0 @@
/*
A C-program for MT19937, with initialization improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Any feedback is very welcome.
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/
#include "mersenne.h"
namespace {
// Period parameters
const int N = 624;
const int M = 397;
const unsigned long MATRIX_A = 0x9908b0dfUL; // Constant vector a
const unsigned long UPPER_MASK = 0x80000000UL; // Most significant w-r bits
const unsigned long LOWER_MASK = 0x7fffffffUL; // Least significant r bits
unsigned long mt[N]; // The array for the state vector
int mti = N + 1; // mti == N+1 means mt[N] is not initialized
// Initializes mt[N] with a seed
void init_genrand(unsigned long s) {
mt[0]= s & 0xffffffffUL;
for (mti = 1; mti < N; mti++)
{
// See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
// In the previous versions, MSBs of the seed affect
// only MSBs of the array mt[].
// 2002/01/09 modified by Makoto Matsumoto
mt[mti] = 1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti;
mt[mti] &= 0xffffffffUL; // For > 32 bit machines
}
}
// Initialize by an array with array-length
// init_key is the array for initializing keys
// key_length is its length
// slight change for C++, 2004/2/26
void init_by_array(unsigned long init_key[], int key_length) {
int i = 1;
int j = 0;
int k = (N > key_length ? N : key_length);
init_genrand(19650218UL);
for ( ; k; k--)
{
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; // Non linear
mt[i] &= 0xffffffffUL; // For WORDSIZE > 32 machines
i++; j++;
if (i >= N)
{
mt[0] = mt[N-1];
i = 1;
}
if (j >= key_length)
j = 0;
}
for (k = N-1; k; k--)
{
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - i; // Non linear
mt[i] &= 0xffffffffUL; // For WORDSIZE > 32 machines
i++;
if (i >= N)
{
mt[0] = mt[N-1];
i = 1;
}
}
mt[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
}
} // namespace
// Generates a random number on [0,0xffffffff]-interval
uint32_t genrand_int32() {
unsigned long y;
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
// Generate N words at one time
if (mti >= N)
{
int kk;
if (mti == N+1) // If init_genrand() has not been called,
init_genrand(5489UL); // a default initial seed is used.
for (kk = 0; kk < N-M; kk++)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for ( ; kk < N-1; kk++)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
uint64_t genrand_int64() {
uint64_t x, y;
x = genrand_int32();
y = genrand_int32();
return (x << 32) | y;
}
void init_mersenne() {
unsigned long init[4] = {0x123, 0x234, 0x345, 0x456};
unsigned long length = 4;
init_by_array(init, length);
}

View File

@@ -1,40 +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(MERSENNE_H_INCLUDED)
#define MERSENNE_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Prototypes
////
extern uint32_t genrand_int32();
extern uint64_t genrand_int64();
extern void init_mersenne();
#endif // !defined(MERSENNE_H_INCLUDED)

View File

@@ -58,7 +58,7 @@ using namespace std;
/// Version number. If this is left empty, the current date (in the format /// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number. /// YYMMDD) is used as a version number.
static const string EngineVersion = "1.9.1"; static const string EngineVersion = "2.0";
static const string AppName = "Stockfish"; static const string AppName = "Stockfish";
static const string AppTag = ""; static const string AppTag = "";
@@ -67,8 +67,8 @@ static const string AppTag = "";
//// Variables //// Variables
//// ////
uint64_t dbg_cnt0 = 0; static uint64_t dbg_cnt0 = 0;
uint64_t dbg_cnt1 = 0; static uint64_t dbg_cnt1 = 0;
bool dbg_show_mean = false; bool dbg_show_mean = false;
bool dbg_show_hit_rate = false; bool dbg_show_hit_rate = false;
@@ -127,42 +127,29 @@ void dbg_print_mean() {
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl; << (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
} }
void dbg_print_hit_rate(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
/// engine_name() returns the full name of the current Stockfish version. /// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the /// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// program was compiled) or "Stockfish <version number>", depending on whether /// the program was compiled) or "Stockfish <version number>", depending
/// the constant EngineVersion (defined in misc.h) is empty. /// on whether the constant EngineVersion (defined in misc.h) is empty.
const string engine_name() { const string engine_name() {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(CpuIs64Bit ? " 64bit" : ""); const string cpu64(CpuIs64Bit ? " 64bit" : "");
if (!EngineVersion.empty()) if (!EngineVersion.empty())
return AppName + " " + EngineVersion + cpu64; return AppName + " " + EngineVersion + cpu64;
string date(__DATE__); // From compiler, format is "Sep 21 2008" stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year;
size_t mon = 1 + months.find(date.substr(0, 3)) / 4; date >> month >> day >> year;
stringstream s; s << setfill('0') << AppName + " " + AppTag + " "
string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2)); << year.substr(2, 2) << setw(2)
<< (1 + months.find(month) / 4) << setw(2)
string name = AppName + " " + AppTag + " "; << day << cpu64;
s << name << date.substr(date.length() - 2) << setfill('0')
<< setw(2) << mon << setw(2) << day << cpu64;
return s.str(); return s.str();
} }
@@ -218,20 +205,18 @@ int cpu_count() {
#endif #endif
/* /// Check for console input. Original code from Beowulf and Olithink
From Beowulf, from Olithink
*/
#ifndef _WIN32 #ifndef _WIN32
/* Non-windows version */
int Bioskey() int data_available()
{ {
fd_set readfds; fd_set readfds;
struct timeval timeout; struct timeval timeout;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds); FD_SET(fileno(stdin), &readfds);
/* Set to timeout immediately */ timeout.tv_sec = 0; // Set to timeout immediately
timeout.tv_sec = 0;
timeout.tv_usec = 0; timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout); select(16, &readfds, 0, 0, &timeout);
@@ -239,59 +224,49 @@ int Bioskey()
} }
#else #else
/* Windows-version */
#include <windows.h>
#include <conio.h>
int Bioskey()
{
static int init = 0,
pipe;
static HANDLE inh;
DWORD dw;
/* If we're running under XBoard then we can't use _kbhit() as the input
* commands are sent to us directly over the internal pipe */
#if defined(FILE_CNT) int data_available()
if (stdin->_cnt > 0) {
return stdin->_cnt; static HANDLE inh = NULL;
#endif static bool usePipe;
if (!init) { INPUT_RECORD rec[256];
init = 1; DWORD dw, recCnt;
if (!inh)
{
inh = GetStdHandle(STD_INPUT_HANDLE); inh = GetStdHandle(STD_INPUT_HANDLE);
pipe = !GetConsoleMode(inh, &dw); usePipe = !GetConsoleMode(inh, &dw);
if (!pipe) { if (!usePipe)
{
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT)); SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh); FlushConsoleInputBuffer(inh);
} }
} }
if (pipe) {
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
return 1;
return dw;
} else {
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &dw);
if (dw <= 0)
return 0;
// Read data from console without removing it from the buffer // If we're running under XBoard then we can't use PeekConsoleInput() as
INPUT_RECORD rec[256]; // the input commands are sent to us directly over the internal pipe.
DWORD recCnt; if (usePipe)
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt)) return PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL) ? dw : 1;
return 0;
// Search for at least one keyboard event // Count the number of unread input records, including keyboard,
for (DWORD i = 0; i < recCnt; i++) // mouse, and window-resizing input records.
if (rec[i].EventType == KEY_EVENT) GetNumberOfConsoleInputEvents(inh, &dw);
return 1;
// Read data from console without removing it from the buffer
if (dw <= 0 || !PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
return 0; 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
/// prefetch() preloads the given address in L1/L2 cache. This is a non /// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be /// blocking function and do not stalls the CPU waiting for data to be
/// loaded from RAM, that can be very slow. /// loaded from RAM, that can be very slow.

View File

@@ -29,7 +29,6 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "application.h"
#include "types.h" #include "types.h"
//// ////
@@ -47,7 +46,7 @@
extern const std::string engine_name(); extern const std::string engine_name();
extern int get_system_time(); extern int get_system_time();
extern int cpu_count(); extern int cpu_count();
extern int Bioskey(); extern int data_available();
extern void prefetch(char* addr); extern void prefetch(char* addr);
extern void prefetchPawn(Key, int); extern void prefetchPawn(Key, int);
@@ -59,9 +58,6 @@ extern void prefetchPawn(Key, int);
extern bool dbg_show_mean; extern bool dbg_show_mean;
extern bool dbg_show_hit_rate; extern bool dbg_show_hit_rate;
extern uint64_t dbg_cnt0;
extern uint64_t dbg_cnt1;
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_before();
@@ -69,7 +65,5 @@ 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_hit_rate();
extern void dbg_print_mean(); extern void dbg_print_mean();
extern void dbg_print_hit_rate(std::ofstream& logFile);
extern void dbg_print_mean(std::ofstream& logFile);
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)

View File

@@ -23,6 +23,7 @@
//// ////
#include <cassert> #include <cassert>
#include <cctype>
#include "move.h" #include "move.h"
#include "piece.h" #include "piece.h"
@@ -33,13 +34,13 @@
//// Functions //// Functions
//// ////
/// move_from_string() takes a position and a string as input, and attempts to /// move_from_uci() takes a position and a string as input, and attempts to
/// convert the string to a move, using simple coordinate notation (g1f3, /// convert the string to a move, using simple coordinate notation (g1f3,
/// a7a8q, etc.). In order to correctly parse en passant captures and castling /// a7a8q, etc.). In order to correctly parse en passant captures and castling
/// moves, we need the position. This function is not robust, and expects that /// moves, we need the position. This function is not robust, and expects that
/// the input move is legal and correctly formatted. /// the input move is legal and correctly formatted.
Move move_from_string(const Position& pos, const std::string& str) { Move move_from_uci(const Position& pos, const std::string& str) {
Square from, to; Square from, to;
Piece piece; Piece piece;
@@ -49,15 +50,15 @@ Move move_from_string(const Position& pos, const std::string& str) {
return MOVE_NONE; return MOVE_NONE;
// Read the from and to squares // Read the from and to squares
from = square_from_string(str.substr(0, 2)); from = make_square(file_from_char(str[0]), rank_from_char(str[1]));
to = square_from_string(str.substr(2, 4)); to = make_square(file_from_char(str[2]), rank_from_char(str[3]));
// Find the moving piece // Find the moving piece
piece = pos.piece_on(from); piece = pos.piece_on(from);
// If the string has more than 4 characters, try to interpret the 5th // If the string has more than 4 characters, try to interpret the 5th
// character as a promotion // character as a promotion.
if (type_of_piece(piece) == PAWN && str.length() > 4) if (str.length() > 4 && piece == piece_of_color_and_type(us, PAWN))
{ {
switch (tolower(str[4])) { switch (tolower(str[4])) {
case 'n': case 'n':
@@ -68,47 +69,47 @@ Move move_from_string(const Position& pos, const std::string& str) {
return make_promotion_move(from, to, ROOK); return make_promotion_move(from, to, ROOK);
case 'q': case 'q':
return make_promotion_move(from, to, QUEEN); return make_promotion_move(from, to, QUEEN);
} }
} }
// En passant move? We assume that a pawn move is an en passant move
// if the destination square is epSquare.
if (to == pos.ep_square() && piece == piece_of_color_and_type(us, PAWN))
return make_ep_move(from, to);
// Is this a castling move? A king move is assumed to be a castling move
// if the destination square is occupied by a friendly rook, or if the
// distance between the source and destination squares is more than 1.
if (piece == piece_of_color_and_type(us, KING)) if (piece == piece_of_color_and_type(us, KING))
{ {
// Is this a castling move? A king move is assumed to be a castling
// move if the destination square is occupied by a friendly rook, or
// if the distance between the source and destination squares is more
// than 1.
if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK)) if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
return make_castle_move(from, to); return make_castle_move(from, to);
else if (square_distance(from, to) > 1) if (square_distance(from, to) > 1)
{ {
// This is a castling move, but we have to translate it to the // This is a castling move, but we have to translate it to the
// internal "king captures rook" representation. // internal "king captures rook" representation.
SquareDelta delta = (to > from ? DELTA_E : DELTA_W); SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
Square s = from + delta; Square s = from;
while (relative_rank(us, s) == RANK_1 && pos.piece_on(s) != piece_of_color_and_type(us, ROOK))
s += delta;
return (relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE); do s += delta;
while ( pos.piece_on(s) != piece_of_color_and_type(us, ROOK)
&& relative_rank(us, s) == RANK_1);
return relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE;
} }
} }
else if (piece == piece_of_color_and_type(us, PAWN))
{
// En passant move? We assume that a pawn move is an en passant move
// without further testing if the destination square is epSquare.
if (to == pos.ep_square())
return make_ep_move(from, to);
}
return make_move(from, to); return make_move(from, to);
} }
/// move_to_string() converts a move to a string in coordinate notation /// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we /// (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 /// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode. /// Chess960 mode.
const std::string move_to_string(Move move, bool chess960) { const std::string move_to_uci(Move move, bool chess960) {
std::string str; std::string str;
Square from = move_from(move); Square from = move_from(move);
@@ -128,7 +129,7 @@ const std::string move_to_string(Move move, bool chess960) {
str = square_to_string(from) + square_to_string(to); str = square_to_string(from) + square_to_string(to);
if (move_is_promotion(move)) if (move_is_promotion(move))
str += piece_type_to_char(move_promotion_piece(move), false); str += char(tolower(piece_type_to_char(move_promotion_piece(move))));
} }
return str; return str;
} }
@@ -139,7 +140,7 @@ const std::string move_to_string(Move move, bool chess960) {
std::ostream& operator << (std::ostream& os, Move m) { std::ostream& operator << (std::ostream& os, Move m) {
bool chess960 = (os.iword(0) != 0); // See set960() bool chess960 = (os.iword(0) != 0); // See set960()
return os << move_to_string(m, chess960); return os << move_to_uci(m, chess960);
} }

View File

@@ -31,6 +31,8 @@
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
// Maximum number of allowed moves per position
const int MOVES_MAX = 256;
//// ////
//// Types //// Types
@@ -62,25 +64,24 @@ struct MoveStack {
int score; int score;
}; };
// Note that operator< is set up such that sorting will be in descending order inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.score; }
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
// An helper insertion sort implementation // An helper insertion sort implementation, works with pointers and iterators
template<typename T> template<typename T, typename K>
inline void insertion_sort(T* firstMove, T* lastMove) inline void insertion_sort(K firstMove, K lastMove)
{ {
T value; T value;
T *cur, *p, *d; K cur, p, d;
if (firstMove != lastMove) if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++) for (cur = firstMove + 1; cur != lastMove; cur++)
{ {
p = d = cur; p = d = cur;
value = *p--; value = *p--;
if (value < *p) if (*p < value)
{ {
do *d = *p; do *d = *p;
while (--d != firstMove && value < *--p); while (--d != firstMove && *--p < value);
*d = value; *d = value;
} }
} }
@@ -115,7 +116,7 @@ inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
} while (p != d); } while (p != d);
// Sort just positive scored moves, remaining only when we get there // Sort just positive scored moves, remaining only when we get there
insertion_sort<T>(firstMove, p); insertion_sort<T, T*>(firstMove, p);
*lastPositive = p; *lastPositive = p;
} }
@@ -130,7 +131,7 @@ inline T pick_best(T* curMove, T* lastMove)
bestMove = *curMove; bestMove = *curMove;
while (++curMove != lastMove) while (++curMove != lastMove)
{ {
if (*curMove < bestMove) if (bestMove < *curMove)
{ {
tmp = *curMove; tmp = *curMove;
*curMove = bestMove; *curMove = bestMove;
@@ -202,8 +203,8 @@ inline Move make_ep_move(Square from, Square to) {
//// ////
extern std::ostream& operator<<(std::ostream& os, Move m); extern std::ostream& operator<<(std::ostream& os, Move m);
extern Move move_from_string(const Position& pos, const std::string &str); extern Move move_from_uci(const Position& pos, const std::string &str);
extern const std::string move_to_string(Move m, bool chess960); extern const std::string move_to_uci(Move m, bool chess960);
extern bool move_is_ok(Move m); extern bool move_is_ok(Move m);

View File

@@ -244,7 +244,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
case QUEEN: case QUEEN:
// In case of a queen remove also squares attacked in the other direction to // In case of a queen remove also squares attacked in the other direction to
// avoid possible illegal moves when queen and king are on adjacent squares. // avoid possible illegal moves when queen and king are on adjacent squares.
if (direction_is_straight(checksq, ksq)) if (RookPseudoAttacks[checksq] & (1ULL << ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq); sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
else else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
@@ -308,7 +308,7 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
bool move_is_legal(const Position& pos, const Move m) { bool move_is_legal(const Position& pos, const Move m) {
MoveStack mlist[256]; MoveStack mlist[MOVES_MAX];
MoveStack *cur, *last = generate_moves(pos, mlist, true); MoveStack *cur, *last = generate_moves(pos, mlist, true);
for (cur = mlist; cur != last; cur++) for (cur = mlist; cur != last; cur++)
@@ -444,152 +444,135 @@ namespace {
return mlist; return mlist;
} }
template<Color Us, SquareDelta Direction> template<SquareDelta Delta>
inline Bitboard move_pawns(Bitboard p) { inline Bitboard move_pawns(Bitboard p) {
if (Direction == DELTA_N) return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
return Us == WHITE ? p << 8 : p >> 8; Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
else if (Direction == DELTA_NE) Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
return Us == WHITE ? p << 9 : p >> 7;
else if (Direction == DELTA_NW)
return Us == WHITE ? p << 7 : p >> 9;
else
return p;
} }
template<Color Us, MoveType Type, SquareDelta Diagonal> template<MoveType Type, SquareDelta Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) { inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
// Calculate our parametrized parameters at compile time const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TFileABB = (Diagonal == DELTA_NE ? FileABB : FileHBB);
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
Bitboard b1, b2; Bitboard b;
Square to; Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces; b = move_pawns<Delta>(pawns) & target & ~TFileABB;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
// Capturing promotions and under-promotions template<Color Us, MoveType Type, SquareDelta Delta>
if (b1 & TRank8BB) 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)
{ {
b2 = b1 & TRank8BB; to = pop_1st_bit(&b);
b1 &= ~TRank8BB;
while (b2) if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{ {
to = pop_1st_bit(&b2); (*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
if (Type == CAPTURE || Type == EVASION) (*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion. It is not sure that
// the promoted knight will give check, but it doesn't worth to verify.
if (Type == CHECK)
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
} }
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == 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
} }
// Serialize standard captures
if (Type == CAPTURE || Type == EVASION)
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
return mlist; return mlist;
} }
template<Color Us, MoveType Type> template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time // Calculate our parametrized parameters at compile time, named
const Color Them = (Us == WHITE ? BLACK : WHITE); // according to the point of view of white side.
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
Square to; Square to;
Bitboard b1, b2, enemyPieces, emptySquares; Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us); Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(Them));
// Standard captures and capturing promotions and underpromotions // Pre-calculate pawn pushes before changing emptySquares definition
if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
{
enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
if (Type == EVASION)
enemyPieces &= target; // Capture only the checker piece
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
}
// Non-capturing promotions and underpromotions
if (pawns & TRank7BB)
{
b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
if (Type == EVASION)
b1 &= target; // Only blocking promotion pushes
while (b1)
{
to = pop_1st_bit(&b1);
if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
}
}
// Standard pawn pushes and double pushes
if (Type != CAPTURE) if (Type != CAPTURE)
{ {
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares()); emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares;
}
// Single and double pawn pushes if (Type == EVASION)
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB; {
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares; emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Filter out unwanted pushes according to the move type // Promotions and underpromotions
if (Type == EVASION) if (pawnsOn7)
{
if (Type == 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 == CAPTURE || Type == 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 != CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
if (Type == CHECK)
{ {
b1 &= target; // Condider only pawn moves which give direct checks
b2 &= target;
}
else if (Type == CHECK)
{
// Pawn moves which give direct cheks
b1 &= pos.attacks_from<PAWN>(ksq, Them); b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them); b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Pawn moves which gives discovered check. This is possible only if // Add pawn moves which gives discovered check. This is possible only
// the pawn is not on the same file as the enemy king, because we // if the pawn is not on the same file as the enemy king, because we
// don't generate captures. // don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard if (pawns & target) // For CHECK type target is dc bitboard
{ {
Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB; dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares;
Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares; dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
@@ -676,7 +659,7 @@ namespace {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
if ( (Side == KING_SIDE && pos.can_castle_kingside(us)) if ( (Side == KING_SIDE && pos.can_castle_kingside(us))
||(Side == QUEEN_SIDE && pos.can_castle_queenside(us))) ||(Side == QUEEN_SIDE && pos.can_castle_queenside(us)))
{ {
Color them = opposite_color(us); Color them = opposite_color(us);

View File

@@ -75,7 +75,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
int searchTT = ttm; int searchTT = ttm;
ttMoves[0].move = ttm; ttMoves[0].move = ttm;
badCaptureThreshold = 0; badCaptureThreshold = 0;
badCaptures = moves + 256; badCaptures = moves + MOVES_MAX;
pinned = p.pinned_pieces(pos.side_to_move()); pinned = p.pinned_pieces(pos.side_to_move());
@@ -99,7 +99,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
phasePtr = MainSearchPhaseTable; phasePtr = MainSearchPhaseTable;
} }
else if (d == DEPTH_ZERO) else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksPhaseTable; phasePtr = QsearchWithChecksPhaseTable;
else else
{ {
@@ -151,7 +151,7 @@ void MovePicker::go_next_phase() {
// Bad captures SEE value is already calculated so just pick // Bad captures SEE value is already calculated so just pick
// them in order to get SEE move ordering. // them in order to get SEE move ordering.
curMove = badCaptures; curMove = badCaptures;
lastMove = moves + 256; lastMove = moves + MOVES_MAX;
return; return;
case PH_EVASIONS: case PH_EVASIONS:
@@ -313,7 +313,7 @@ Move MovePicker::get_next_move() {
// Sort negative scored moves only when we get there // Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture) if (curMove == lastGoodNonCapture)
insertion_sort(lastGoodNonCapture, lastMove); insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move; move = (curMove++)->move;
if ( move != ttMoves[0].move if ( move != ttMoves[0].move

View File

@@ -66,7 +66,7 @@ private:
int badCaptureThreshold, phase; int badCaptureThreshold, phase;
const uint8_t* phasePtr; const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures; MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack moves[256]; MoveStack moves[MOVES_MAX];
}; };

View File

@@ -40,23 +40,26 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by file // Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[8] = { const Score DoubledPawnPenalty[2][8] = {
S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
}; { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by file // Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[8] = { const Score IsolatedPawnPenalty[2][8] = {
S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
}; { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by file // Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[8] = { const Score BackwardPawnPenalty[2][8] = {
S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
}; { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file // Pawn chain membership bonus by file
const Score ChainBonus[8] = { const Score ChainBonus[8] = {
@@ -88,8 +91,9 @@ PawnInfoTable::PawnInfoTable() {
{ {
std::cerr << "Failed to allocate " << (PawnTableSize * sizeof(PawnInfo)) std::cerr << "Failed to allocate " << (PawnTableSize * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl; << " bytes for pawn hash table." << std::endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
memset(entries, 0, PawnTableSize * sizeof(PawnInfo));
} }
@@ -120,18 +124,19 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
// Clear the PawnInfo object, and set the key // Clear the PawnInfo object, and set the key
memset(pi, 0, sizeof(PawnInfo)); memset(pi, 0, sizeof(PawnInfo));
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE; pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->key = key; pi->key = key;
// Calculate pawn attacks // Calculate pawn attacks
Bitboard whitePawns = pos.pieces(PAWN, WHITE); Bitboard wPawns = pos.pieces(PAWN, WHITE);
Bitboard blackPawns = pos.pieces(PAWN, BLACK); Bitboard bPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB); pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB); pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors // Evaluate pawns for both colors
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi) pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi); - evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
return pi; return pi;
} }
@@ -150,11 +155,6 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15; const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Square* ptr = pos.piece_list_begin(Us, PAWN); const Square* ptr = pos.piece_list_begin(Us, PAWN);
// Initialize halfOpenFiles[]
for (f = FILE_A; f <= FILE_H; f++)
if (!(ourPawns & file_bb(f)))
pi->halfOpenFiles[Us] |= (1 << f);
// 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 = *ptr++) != SQ_NONE)
{ {
@@ -163,13 +163,16 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
f = square_file(s); f = square_file(s);
r = square_rank(s); r = square_rank(s);
// This file cannot be half open
pi->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));
// Passed, isolated, doubled or member of a pawn // Passed, isolated, doubled or member of a pawn
// chain (but not the backward one) ? // chain (but not the backward one) ?
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
doubled = ourPawns & squares_behind(Us, s); doubled = ourPawns & squares_in_front_of(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s); opposed = theirPawns & squares_in_front_of(Us, s);
isolated = !(ourPawns & neighboring_files_bb(f)); isolated = !(ourPawns & neighboring_files_bb(f));
chain = ourPawns & neighboring_files_bb(f) & b; chain = ourPawns & neighboring_files_bb(f) & b;
@@ -209,40 +212,27 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
&& (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB && (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns); && count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
// In order to prevent doubled passed pawns from receiving a too big
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if (passed && (ourPawns & squares_in_front_of(Us, s)))
passed = false;
// Mark the pawn as passed. Pawn will be properly scored in evaluation // Mark the pawn as passed. Pawn will be properly scored in evaluation
// because we need full attack info to evaluate passed pawns. // because we need full attack info to evaluate passed pawns. Only the
if (passed) // frontmost passed pawn on each file is considered a true passed pawn.
if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s); set_bit(&(pi->passedPawns[Us]), s);
// Score this pawn // Score this pawn
if (isolated) if (isolated)
{ value -= IsolatedPawnPenalty[opposed][f];
value -= IsolatedPawnPenalty[f];
if (!opposed)
value -= IsolatedPawnPenalty[f] / 2;
}
if (doubled) if (doubled)
value -= DoubledPawnPenalty[f]; value -= DoubledPawnPenalty[opposed][f];
if (backward) if (backward)
{ value -= BackwardPawnPenalty[opposed][f];
value -= BackwardPawnPenalty[f];
if (!opposed)
value -= BackwardPawnPenalty[f] / 2;
}
if (chain) if (chain)
value += ChainBonus[f]; value += ChainBonus[f];
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidateBonus[relative_rank(Us, s)];
} }
return value; return value;
} }

View File

@@ -78,6 +78,9 @@ class PawnInfoTable {
enum SideType { KingSide, QueenSide }; enum SideType { KingSide, QueenSide };
PawnInfoTable(const PawnInfoTable&);
PawnInfoTable& operator=(const PawnInfoTable&);
public: public:
PawnInfoTable(); PawnInfoTable();
~PawnInfoTable(); ~PawnInfoTable();

View File

@@ -1,76 +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/>.
*/
////
//// Includes
////
#include <string>
#include "piece.h"
using namespace std;
// Tables indexed by Piece
const Value PieceValueMidgame[17] = {
VALUE_ZERO,
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO
};
const Value PieceValueEndgame[17] = {
VALUE_ZERO,
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO
};
const int SlidingArray[18] = {
0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
};
////
//// Functions
////
/// Translating piece types to/from English piece letters
static const string PieceChars(" pnbrqk PNBRQK");
char piece_type_to_char(PieceType pt, bool upcase) {
return PieceChars[pt + int(upcase) * 7];
}
PieceType piece_type_from_char(char c) {
size_t idx = PieceChars.find(c);
return idx != string::npos ? PieceType(idx % 7) : PIECE_TYPE_NONE;
}

View File

@@ -25,6 +25,8 @@
//// Includes //// Includes
//// ////
#include <string>
#include "color.h" #include "color.h"
#include "square.h" #include "square.h"
#include "value.h" #include "value.h"
@@ -69,10 +71,6 @@ const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9); const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE); const Value QueenValueEndgame = Value(0x9FE);
extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
extern const int SlidingArray[18];
//// ////
//// Inline functions //// Inline functions
@@ -90,29 +88,24 @@ inline Piece piece_of_color_and_type(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt)); return Piece((int(c) << 3) | int(pt));
} }
inline int piece_is_slider(Piece p) {
return SlidingArray[p];
}
inline SquareDelta pawn_push(Color c) { inline SquareDelta pawn_push(Color c) {
return (c == WHITE ? DELTA_N : DELTA_S); return (c == WHITE ? DELTA_N : DELTA_S);
} }
inline bool piece_type_is_ok(PieceType pc) { inline bool piece_type_is_ok(PieceType pt) {
return pc >= PAWN && pc <= KING; return pt >= PAWN && pt <= KING;
} }
inline bool piece_is_ok(Piece pc) { inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(pc)) && color_is_ok(color_of_piece(pc)); return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
} }
inline char piece_type_to_char(PieceType pt) {
return std::string(" PNBRQK")[pt];
}
//// inline PieceType piece_type_from_char(char c) {
//// Prototypes return PieceType(std::string(" PNBRQK").find(c));
//// }
extern char piece_type_to_char(PieceType pt, bool upcase = false);
extern PieceType piece_type_from_char(char c);
#endif // !defined(PIECE_H_INCLUDED) #endif // !defined(PIECE_H_INCLUDED)

View File

@@ -31,11 +31,11 @@
#include <sstream> #include <sstream>
#include "bitcount.h" #include "bitcount.h"
#include "mersenne.h"
#include "movegen.h" #include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "position.h" #include "position.h"
#include "psqtab.h" #include "psqtab.h"
#include "rkiss.h"
#include "san.h" #include "san.h"
#include "tt.h" #include "tt.h"
#include "ucioption.h" #include "ucioption.h"
@@ -44,43 +44,11 @@ using std::string;
using std::cout; using std::cout;
using std::endl; using std::endl;
static inline bool isZero(char c) { return c == '0'; }
struct PieceLetters : public std::map<char, Piece> {
PieceLetters() {
operator[]('K') = WK; operator[]('k') = BK;
operator[]('Q') = WQ; operator[]('q') = BQ;
operator[]('R') = WR; operator[]('r') = BR;
operator[]('B') = WB; operator[]('b') = BB;
operator[]('N') = WN; operator[]('n') = BN;
operator[]('P') = WP; operator[]('p') = BP;
operator[](' ') = PIECE_NONE; operator[]('.') = PIECE_NONE_DARK_SQ;
}
char from_piece(Piece p) const {
std::map<char, Piece>::const_iterator it;
for (it = begin(); it != end(); ++it)
if (it->second == p)
return it->first;
assert(false);
return 0;
}
};
//// ////
//// Constants and variables //// Position's static data definitions
//// ////
/// Bonus for having the side to move (modified by Joona Kiiski)
static const Score TempoValue = make_score(48, 22);
Key Position::zobrist[2][8][64]; Key Position::zobrist[2][8][64];
Key Position::zobEp[64]; Key Position::zobEp[64];
Key Position::zobCastle[16]; Key Position::zobCastle[16];
@@ -89,16 +57,71 @@ Key Position::zobExclusion;
Score Position::PieceSquareTable[16][64]; Score Position::PieceSquareTable[16][64];
static PieceLetters pieceLetters; // Material values arrays, indexed by Piece
const Value Position::PieceValueMidgame[17] = {
VALUE_ZERO,
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO,
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame
};
// Material values used by SEE, indexed by PieceType const Value Position::PieceValueEndgame[17] = {
VALUE_ZERO,
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame, VALUE_ZERO,
VALUE_ZERO, VALUE_ZERO,
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame
};
// Material values array used by SEE, indexed by PieceType
const Value Position::seeValues[] = { const Value Position::seeValues[] = {
VALUE_ZERO, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, VALUE_ZERO,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10 PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10
}; };
/// Constructors namespace {
// Bonus for having the side to move (modified by Joona Kiiski)
const Score TempoValue = make_score(48, 22);
bool isZero(char c) { return c == '0'; }
struct PieceLetters : public std::map<char, Piece> {
PieceLetters() {
operator[]('K') = WK; operator[]('k') = BK;
operator[]('Q') = WQ; operator[]('q') = BQ;
operator[]('R') = WR; operator[]('r') = BR;
operator[]('B') = WB; operator[]('b') = BB;
operator[]('N') = WN; operator[]('n') = BN;
operator[]('P') = WP; operator[]('p') = BP;
operator[](' ') = PIECE_NONE;
operator[]('.') = PIECE_NONE_DARK_SQ;
}
char from_piece(Piece p) const {
std::map<char, Piece>::const_iterator it;
for (it = begin(); it != end(); ++it)
if (it->second == p)
return it->first;
assert(false);
return 0;
}
};
PieceLetters pieceLetters;
}
/// CheckInfo c'tor
CheckInfo::CheckInfo(const Position& pos) { CheckInfo::CheckInfo(const Position& pos) {
@@ -121,13 +144,12 @@ CheckInfo::CheckInfo(const Position& pos) {
/// or the FEN string, we want the new born Position object do not depend /// or the FEN string, we want the new born Position object do not depend
/// on any external data so we detach state pointer from the source one. /// on any external data so we detach state pointer from the source one.
Position::Position(int th) : threadID(th) {}
Position::Position(const Position& pos, int th) { Position::Position(const Position& pos, int th) {
memcpy(this, &pos, sizeof(Position)); memcpy(this, &pos, sizeof(Position));
detach(); // Always detach() in copy c'tor to avoid surprises detach(); // Always detach() in copy c'tor to avoid surprises
threadID = th; threadID = th;
nodes = 0;
} }
Position::Position(const string& fen, int th) { Position::Position(const string& fen, int th) {
@@ -293,7 +315,7 @@ bool Position::set_castling_rights(char token) {
for (Square sq = sqH; sq >= sqA; sq--) for (Square sq = sqH; sq >= sqA; sq--)
if (piece_on(sq) == rook) if (piece_on(sq) == rook)
{ {
allow_oo(c); do_allow_oo(c);
initialKRFile = square_file(sq); initialKRFile = square_file(sq);
break; break;
} }
@@ -303,7 +325,7 @@ bool Position::set_castling_rights(char token) {
for (Square sq = sqA; sq <= sqH; sq++) for (Square sq = sqA; sq <= sqH; sq++)
if (piece_on(sq) == rook) if (piece_on(sq) == rook)
{ {
allow_ooo(c); do_allow_ooo(c);
initialQRFile = square_file(sq); initialQRFile = square_file(sq);
break; break;
} }
@@ -313,12 +335,12 @@ bool Position::set_castling_rights(char token) {
File rookFile = File(token - 'A') + FILE_A; File rookFile = File(token - 'A') + FILE_A;
if (rookFile < initialKFile) if (rookFile < initialKFile)
{ {
allow_ooo(c); do_allow_ooo(c);
initialQRFile = rookFile; initialQRFile = rookFile;
} }
else else
{ {
allow_oo(c); do_allow_oo(c);
initialKRFile = rookFile; initialKRFile = rookFile;
} }
} }
@@ -382,7 +404,7 @@ const string Position::to_fen() const {
/// Position::print() prints an ASCII representation of the position to /// Position::print() prints an ASCII representation of the position to
/// the standard output. If a move is given then also the san is print. /// the standard output. If a move is given then also the san is printed.
void Position::print(Move move) const { void Position::print(Move move) const {
@@ -502,16 +524,24 @@ Bitboard Position::attacks_from(Piece p, Square s) const {
switch (p) switch (p)
{ {
case WP: return attacks_from<PAWN>(s, WHITE);
case BP: return attacks_from<PAWN>(s, BLACK);
case WN: case BN: return attacks_from<KNIGHT>(s);
case WB: case BB: return attacks_from<BISHOP>(s); case WB: case BB: return attacks_from<BISHOP>(s);
case WR: case BR: return attacks_from<ROOK>(s); case WR: case BR: return attacks_from<ROOK>(s);
case WQ: case BQ: return attacks_from<QUEEN>(s); case WQ: case BQ: return attacks_from<QUEEN>(s);
case WK: case BK: return attacks_from<KING>(s); default: return StepAttackBB[p][s];
default: break; }
}
Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
assert(square_is_ok(s));
switch (p)
{
case WB: case BB: return bishop_attacks_bb(s, occ);
case WR: case BR: return rook_attacks_bb(s, occ);
case WQ: case BQ: return bishop_attacks_bb(s, occ) | rook_attacks_bb(s, occ);
default: return StepAttackBB[p][s];
} }
return false;
} }
@@ -523,6 +553,7 @@ bool Position::move_attacks_square(Move m, Square s) const {
assert(move_is_ok(m)); assert(move_is_ok(m));
assert(square_is_ok(s)); assert(square_is_ok(s));
Bitboard occ, xray;
Square f = move_from(m), t = move_to(m); Square f = move_from(m), t = move_to(m);
assert(square_is_occupied(f)); assert(square_is_occupied(f));
@@ -531,12 +562,11 @@ bool Position::move_attacks_square(Move m, Square s) const {
return true; return true;
// Move the piece and scan for X-ray attacks behind it // Move the piece and scan for X-ray attacks behind it
Bitboard occ = occupied_squares(); occ = occupied_squares();
Color us = color_of_piece_on(f); do_move_bb(&occ, make_move_bb(f, t));
clear_bit(&occ, f); xray = ( (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN))
set_bit(&occ, t); |(bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN)))
Bitboard xray = ( (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN)) & pieces_of_color(color_of_piece_on(f));
|(bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN))) & pieces_of_color(us);
// If we have attacks we need to verify that are caused by our move // If we have attacks we need to verify that are caused by our move
// and are not already existent ones. // and are not already existent ones.
@@ -569,22 +599,18 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
if (move_is_castle(m)) if (move_is_castle(m))
return true; return true;
Color us = side_to_move();
Square from = move_from(m);
assert(color_of_piece_on(from) == us);
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
// En passant captures are a tricky special case. Because they are // En passant captures are a tricky special case. Because they are
// rather uncommon, we do it simply by testing whether the king is attacked // rather uncommon, we do it simply by testing whether the king is attacked
// after the move is made // after the move is made
if (move_is_ep(m)) if (move_is_ep(m))
{ {
Color us = side_to_move();
Color them = opposite_color(us); Color them = opposite_color(us);
Square from = move_from(m);
Square to = move_to(m); Square to = move_to(m);
Square capsq = make_square(square_file(to), square_rank(from)); Square capsq = make_square(square_file(to), square_rank(from));
Bitboard b = occupied_squares();
Square ksq = king_square(us); Square ksq = king_square(us);
Bitboard b = occupied_squares();
assert(to == ep_square()); assert(to == ep_square());
assert(piece_on(from) == piece_of_color_and_type(us, PAWN)); assert(piece_on(from) == piece_of_color_and_type(us, PAWN));
@@ -599,6 +625,12 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
&& !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them)); && !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them));
} }
Color us = side_to_move();
Square from = move_from(m);
assert(color_of_piece_on(from) == us);
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
// If the moving piece is a king, check whether the destination // If the moving piece is a king, check whether the destination
// square is attacked by the opponent. // square is attacked by the opponent.
if (type_of_piece_on(from) == KING) if (type_of_piece_on(from) == KING)
@@ -606,9 +638,9 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
// A non-king move is legal if and only if it is not pinned or it // A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king. // is moving along the ray towards or away from the king.
return ( !pinned return !pinned
|| !bit_is_set(pinned, from) || !bit_is_set(pinned, from)
|| (direction_between_squares(from, king_square(us)) == direction_between_squares(move_to(m), king_square(us)))); || squares_aligned(from, move_to(m), king_square(us));
} }
@@ -666,7 +698,7 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const {
{ {
// For pawn and king moves we need to verify also direction // For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING) if ( (pt != PAWN && pt != KING)
||(direction_between_squares(from, ci.ksq) != direction_between_squares(to, ci.ksq))) || !squares_aligned(from, to, ci.ksq))
return true; return true;
} }
@@ -752,6 +784,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(is_ok()); assert(is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
nodes++;
Key key = st->key; Key key = st->key;
// Copy some fields of old state to our new StateInfo object except the // Copy some fields of old state to our new StateInfo object except the
@@ -765,7 +798,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Value npMaterial[2]; Value npMaterial[2];
}; };
memcpy(&newSt, st, sizeof(ReducedStateInfo)); if (&newSt != st)
memcpy(&newSt, st, sizeof(ReducedStateInfo));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -1369,7 +1404,7 @@ int Position::see_sign(Move m) const {
int Position::see(Square from, Square to) const { int Position::see(Square from, Square to) const {
Bitboard occ, attackers, stmAttackers, b; Bitboard occupied, attackers, stmAttackers, b;
int swapList[32], slIndex = 1; int swapList[32], slIndex = 1;
PieceType capturedType, pt; PieceType capturedType, pt;
Color stm; Color stm;
@@ -1383,30 +1418,30 @@ int Position::see(Square from, Square to) const {
if (capturedType == KING) if (capturedType == KING)
return seeValues[capturedType]; return seeValues[capturedType];
occ = occupied_squares(); occupied = occupied_squares();
// Handle en passant moves // Handle en passant moves
if (st->epSquare == to && type_of_piece_on(from) == PAWN) if (st->epSquare == to && type_of_piece_on(from) == PAWN)
{ {
Square capQq = (side_to_move() == WHITE) ? (to - DELTA_N) : (to - DELTA_S); Square capQq = (side_to_move() == WHITE ? to - DELTA_N : to - DELTA_S);
assert(capturedType == PIECE_TYPE_NONE); assert(capturedType == PIECE_TYPE_NONE);
assert(type_of_piece_on(capQq) == PAWN); assert(type_of_piece_on(capQq) == PAWN);
// Remove the captured pawn // Remove the captured pawn
clear_bit(&occ, capQq); clear_bit(&occupied, capQq);
capturedType = PAWN; capturedType = PAWN;
} }
// Find all attackers to the destination square, with the moving piece // Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it. // removed, but possibly an X-ray attacker added behind it.
clear_bit(&occ, from); clear_bit(&occupied, from);
attackers = (rook_attacks_bb(to, occ) & pieces(ROOK, QUEEN)) attackers = (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN))
| (bishop_attacks_bb(to, occ) & pieces(BISHOP, QUEEN)) | (bishop_attacks_bb(to, occupied)& pieces(BISHOP, QUEEN))
| (attacks_from<KNIGHT>(to) & pieces(KNIGHT)) | (attacks_from<KNIGHT>(to) & pieces(KNIGHT))
| (attacks_from<KING>(to) & pieces(KING)) | (attacks_from<KING>(to) & pieces(KING))
| (attacks_from<PAWN>(to, WHITE) & pieces(PAWN, BLACK)) | (attacks_from<PAWN>(to, WHITE) & pieces(PAWN, BLACK))
| (attacks_from<PAWN>(to, BLACK) & pieces(PAWN, WHITE)); | (attacks_from<PAWN>(to, BLACK) & pieces(PAWN, WHITE));
// If the opponent has no attackers we are finished // If the opponent has no attackers we are finished
stm = opposite_color(color_of_piece_on(from)); stm = opposite_color(color_of_piece_on(from));
@@ -1430,28 +1465,28 @@ int Position::see(Square from, Square to) const {
for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++) for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++)
assert(pt < KING); assert(pt < KING);
// Remove the attacker we just found from the 'attackers' bitboard, // Remove the attacker we just found from the 'occupied' bitboard,
// and scan for new X-ray attacks behind the attacker. // and scan for new X-ray attacks behind the attacker.
b = stmAttackers & pieces(pt); b = stmAttackers & pieces(pt);
occ ^= (b & (~b + 1)); occupied ^= (b & (~b + 1));
attackers |= (rook_attacks_bb(to, occ) & pieces(ROOK, QUEEN)) attackers |= (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN))
| (bishop_attacks_bb(to, occ) & pieces(BISHOP, QUEEN)); | (bishop_attacks_bb(to, occupied) & pieces(BISHOP, QUEEN));
attackers &= occ; // Cut out pieces we've already done attackers &= occupied; // Cut out pieces we've already done
// Add the new entry to the swap list // Add the new entry to the swap list
assert(slIndex < 32); assert(slIndex < 32);
swapList[slIndex] = -swapList[slIndex - 1] + seeValues[capturedType]; swapList[slIndex] = -swapList[slIndex - 1] + seeValues[capturedType];
slIndex++; slIndex++;
// Remember the value of the capturing piece, and change the side to move // Remember the value of the capturing piece, and change the side to
// before beginning the next iteration // move before beginning the next iteration.
capturedType = pt; capturedType = pt;
stm = opposite_color(stm); stm = opposite_color(stm);
stmAttackers = attackers & pieces_of_color(stm); stmAttackers = attackers & pieces_of_color(stm);
// Stop after a king capture // Stop before processing a king capture
if (pt == KING && stmAttackers) if (capturedType == KING && stmAttackers)
{ {
assert(slIndex < 32); assert(slIndex < 32);
swapList[slIndex++] = QueenValueMidgame*10; swapList[slIndex++] = QueenValueMidgame*10;
@@ -1460,7 +1495,7 @@ int Position::see(Square from, Square to) const {
} while (stmAttackers); } while (stmAttackers);
// Having built the swap list, we negamax through it to find the best // Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move // achievable score from the point of view of the side to move.
while (--slIndex) while (--slIndex)
swapList[slIndex-1] = Min(-swapList[slIndex], swapList[slIndex-1]); swapList[slIndex-1] = Min(-swapList[slIndex], swapList[slIndex-1]);
@@ -1477,6 +1512,7 @@ void Position::clear() {
memset(st, 0, sizeof(StateInfo)); memset(st, 0, sizeof(StateInfo));
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
startPosPlyCounter = 0; startPosPlyCounter = 0;
nodes = 0;
memset(byColorBB, 0, sizeof(Bitboard) * 2); memset(byColorBB, 0, sizeof(Bitboard) * 2);
memset(byTypeBB, 0, sizeof(Bitboard) * 8); memset(byTypeBB, 0, sizeof(Bitboard) * 8);
@@ -1517,7 +1553,7 @@ void Position::inc_startpos_ply_counter() {
} }
/// Position::put_piece() puts a piece on the given square of the board, /// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, bitboards, and piece counts. /// updating the board array, pieces list, bitboards, and piece counts.
void Position::put_piece(Piece p, Square s) { void Position::put_piece(Piece p, Square s) {
@@ -1525,32 +1561,12 @@ void Position::put_piece(Piece p, Square s) {
PieceType pt = type_of_piece(p); PieceType pt = type_of_piece(p);
board[s] = p; board[s] = p;
index[s] = pieceCount[c][pt]; index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s; pieceList[c][pt][index[s]] = s;
set_bit(&(byTypeBB[pt]), s); set_bit(&(byTypeBB[pt]), s);
set_bit(&(byColorBB[c]), s); set_bit(&(byColorBB[c]), s);
set_bit(&byTypeBB[0], s); // HACK: byTypeBB[0] contains all occupied squares. set_bit(&(byTypeBB[0]), s); // HACK: byTypeBB[0] contains all occupied squares.
pieceCount[c][pt]++;
}
/// Position::allow_oo() gives the given side the right to castle kingside.
/// Used when setting castling rights during parsing of FEN strings.
void Position::allow_oo(Color c) {
st->castleRights |= (1 + int(c));
}
/// Position::allow_ooo() gives the given side the right to castle queenside.
/// Used when setting castling rights during parsing of FEN strings.
void Position::allow_ooo(Color c) {
st->castleRights |= (4 + 4*int(c));
} }
@@ -1561,7 +1577,7 @@ void Position::allow_ooo(Color c) {
Key Position::compute_key() const { Key Position::compute_key() const {
Key result = Key(0ULL); Key result = zobCastle[st->castleRights];
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
if (square_is_occupied(s)) if (square_is_occupied(s))
@@ -1570,7 +1586,6 @@ Key Position::compute_key() const {
if (ep_square() != SQ_NONE) if (ep_square() != SQ_NONE)
result ^= zobEp[ep_square()]; result ^= zobEp[ep_square()];
result ^= zobCastle[st->castleRights];
if (side_to_move() == BLACK) if (side_to_move() == BLACK)
result ^= zobSideToMove; result ^= zobSideToMove;
@@ -1586,18 +1601,14 @@ Key Position::compute_key() const {
Key Position::compute_pawn_key() const { Key Position::compute_pawn_key() const {
Key result = Key(0ULL);
Bitboard b; Bitboard b;
Square s; Key result = 0;
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
{ {
b = pieces(PAWN, c); b = pieces(PAWN, c);
while (b) while (b)
{ result ^= zobrist[c][PAWN][pop_1st_bit(&b)];
s = pop_1st_bit(&b);
result ^= zobrist[c][PAWN][s];
}
} }
return result; return result;
} }
@@ -1611,11 +1622,13 @@ Key Position::compute_pawn_key() const {
Key Position::compute_material_key() const { Key Position::compute_material_key() const {
Key result = Key(0ULL); int count;
Key result = 0;
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (PieceType pt = PAWN; pt <= QUEEN; pt++)
{ {
int count = piece_count(c, pt); count = piece_count(c, pt);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
result ^= zobrist[c][pt][i]; result ^= zobrist[c][pt][i];
} }
@@ -1629,20 +1642,15 @@ Key Position::compute_material_key() const {
/// updated by do_move and undo_move when the program is running in debug mode. /// updated by do_move and undo_move when the program is running in debug mode.
Score Position::compute_value() const { Score Position::compute_value() const {
Score result = SCORE_ZERO;
Bitboard b; Bitboard b;
Square s; Score result = SCORE_ZERO;
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; pt++)
{ {
b = pieces(pt, c); b = pieces(pt, c);
while (b) while (b)
{ result += pst(c, pt, pop_1st_bit(&b));
s = pop_1st_bit(&b);
assert(piece_on(s) == piece_of_color_and_type(c, pt));
result += pst(c, pt, s);
}
} }
result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2); result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2);
@@ -1651,7 +1659,7 @@ Score Position::compute_value() const {
/// Position::compute_non_pawn_material() computes the total non-pawn middle /// Position::compute_non_pawn_material() computes the total non-pawn middle
/// game material score for the given side. Material scores are updated /// game material value for the given side. Material values are updated
/// incrementally during the search, this function is only used while /// incrementally during the search, this function is only used while
/// initializing a new Position object. /// initializing a new Position object.
@@ -1660,16 +1668,8 @@ Value Position::compute_non_pawn_material(Color c) const {
Value result = VALUE_ZERO; Value result = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
{ result += piece_count(c, pt) * PieceValueMidgame[pt];
Bitboard b = pieces(pt, c);
while (b)
{
assert(piece_on(first_1(b)) == piece_of_color_and_type(c, pt));
pop_1st_bit(&b);
result += PieceValueMidgame[pt];
}
}
return result; return result;
} }
@@ -1703,8 +1703,8 @@ bool Position::is_draw() const {
bool Position::is_mate() const { bool Position::is_mate() const {
MoveStack moves[256]; MoveStack moves[MOVES_MAX];
return is_check() && (generate_moves(*this, moves) == moves); return is_check() && generate_moves(*this, moves) == moves;
} }
@@ -1713,7 +1713,7 @@ bool Position::is_mate() const {
bool Position::has_mate_threat() { bool Position::has_mate_threat() {
MoveStack mlist[256], *last, *cur; MoveStack mlist[MOVES_MAX], *last, *cur;
StateInfo st1, st2; StateInfo st1, st2;
bool mateFound = false; bool mateFound = false;
@@ -1724,7 +1724,7 @@ bool Position::has_mate_threat() {
// First pass the move to our opponent doing a null move // First pass the move to our opponent doing a null move
do_null_move(st1); do_null_move(st1);
// Then generate pseudo-legal moves that give check // Then generate pseudo-legal moves that could give check
last = generate_non_capture_checks(*this, mlist); last = generate_non_capture_checks(*this, mlist);
last = generate_captures(*this, last); last = generate_captures(*this, last);
@@ -1757,18 +1757,19 @@ bool Position::has_mate_threat() {
void Position::init_zobrist() { void Position::init_zobrist() {
int i,j, k; int i,j, k;
RKISS rk;
for (i = 0; i < 2; i++) for (j = 0; j < 8; j++) for (k = 0; k < 64; k++) for (i = 0; i < 2; i++) for (j = 0; j < 8; j++) for (k = 0; k < 64; k++)
zobrist[i][j][k] = Key(genrand_int64()); zobrist[i][j][k] = rk.rand<Key>();
for (i = 0; i < 64; i++) for (i = 0; i < 64; i++)
zobEp[i] = Key(genrand_int64()); zobEp[i] = rk.rand<Key>();
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
zobCastle[i] = Key(genrand_int64()); zobCastle[i] = rk.rand<Key>();
zobSideToMove = Key(genrand_int64()); zobSideToMove = rk.rand<Key>();
zobExclusion = Key(genrand_int64()); zobExclusion = rk.rand<Key>();
} }
@@ -1804,16 +1805,16 @@ void Position::flipped_copy(const Position& pos) {
// Board // Board
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.square_is_empty(s)) if (!pos.square_is_empty(s))
put_piece(Piece(int(pos.piece_on(s)) ^ 8), flip_square(s)); put_piece(Piece(pos.piece_on(s) ^ 8), flip_square(s));
// Side to move // Side to move
sideToMove = opposite_color(pos.side_to_move()); sideToMove = opposite_color(pos.side_to_move());
// Castling rights // Castling rights
if (pos.can_castle_kingside(WHITE)) allow_oo(BLACK); if (pos.can_castle_kingside(WHITE)) do_allow_oo(BLACK);
if (pos.can_castle_queenside(WHITE)) allow_ooo(BLACK); if (pos.can_castle_queenside(WHITE)) do_allow_ooo(BLACK);
if (pos.can_castle_kingside(BLACK)) allow_oo(WHITE); if (pos.can_castle_kingside(BLACK)) do_allow_oo(WHITE);
if (pos.can_castle_queenside(BLACK)) allow_ooo(WHITE); if (pos.can_castle_queenside(BLACK)) do_allow_ooo(WHITE);
initialKFile = pos.initialKFile; initialKFile = pos.initialKFile;
initialKRFile = pos.initialKRFile; initialKRFile = pos.initialKRFile;
@@ -1855,18 +1856,20 @@ void Position::flipped_copy(const Position& pos) {
bool Position::is_ok(int* failedStep) const { bool Position::is_ok(int* failedStep) const {
// What features of the position should be verified? // What features of the position should be verified?
static const bool debugBitboards = false; const bool debugAll = false;
static const bool debugKingCount = false;
static const bool debugKingCapture = false; const bool debugBitboards = debugAll || false;
static const bool debugCheckerCount = false; const bool debugKingCount = debugAll || false;
static const bool debugKey = false; const bool debugKingCapture = debugAll || false;
static const bool debugMaterialKey = false; const bool debugCheckerCount = debugAll || false;
static const bool debugPawnKey = false; const bool debugKey = debugAll || false;
static const bool debugIncrementalEval = false; const bool debugMaterialKey = debugAll || false;
static const bool debugNonPawnMaterial = false; const bool debugPawnKey = debugAll || false;
static const bool debugPieceCounts = false; const bool debugIncrementalEval = debugAll || false;
static const bool debugPieceList = false; const bool debugNonPawnMaterial = debugAll || false;
static const bool debugCastleSquares = false; const bool debugPieceCounts = debugAll || false;
const bool debugPieceList = debugAll || false;
const bool debugCastleSquares = debugAll || false;
if (failedStep) *failedStep = 1; if (failedStep) *failedStep = 1;

View File

@@ -21,25 +21,12 @@
#if !defined(POSITION_H_INCLUDED) #if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
// Disable some silly and noisy warning from MSVC compiler
#if defined(_MSC_VER)
// Forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable: 4800)
// Conditional expression is constant
#pragma warning(disable: 4127)
#endif
//// ////
//// Includes //// Includes
//// ////
#include "bitboard.h" #include "bitboard.h"
#include "color.h" #include "color.h"
#include "direction.h"
#include "move.h" #include "move.h"
#include "piece.h" #include "piece.h"
#include "square.h" #include "square.h"
@@ -146,7 +133,6 @@ public:
}; };
// Constructors // Constructors
explicit Position(int threadID);
Position(const Position& pos, int threadID); Position(const Position& pos, int threadID);
Position(const std::string& fen, int threadID); Position(const std::string& fen, int threadID);
@@ -210,6 +196,7 @@ public:
// Information about attacks to or from a given square // Information about attacks to or from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) 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);
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;
@@ -278,8 +265,9 @@ public:
// Reset the gamePly variable to 0 // Reset the gamePly variable to 0
void reset_game_ply(); void reset_game_ply();
void inc_startpos_ply_counter(); void inc_startpos_ply_counter();
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
// Position consistency check, for debugging // Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const; bool is_ok(int* failedStep = NULL) const;
@@ -293,8 +281,8 @@ private:
// Initialization helper functions (used while setting up a position) // Initialization helper functions (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void allow_oo(Color c); void do_allow_oo(Color c);
void allow_ooo(Color c); void do_allow_ooo(Color c);
bool set_castling_rights(char token); bool set_castling_rights(char token);
// Helper functions for doing and undoing moves // Helper functions for doing and undoing moves
@@ -338,6 +326,7 @@ private:
bool isChess960; bool isChess960;
int startPosPlyCounter; int startPosPlyCounter;
int threadID; int threadID;
int64_t nodes;
StateInfo* st; StateInfo* st;
// Static variables // Static variables
@@ -348,6 +337,8 @@ private:
static Score PieceSquareTable[16][64]; static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
static const Value seeValues[8]; static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
}; };
@@ -355,6 +346,14 @@ private:
//// Inline functions //// Inline functions
//// ////
inline int64_t Position::nodes_searched() const {
return nodes;
}
inline void Position::set_nodes_searched(int64_t n) {
nodes = n;
}
inline Piece Position::piece_on(Square s) const { inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
@@ -392,7 +391,7 @@ inline Bitboard Position::occupied_squares() const {
} }
inline Bitboard Position::empty_squares() const { inline Bitboard Position::empty_squares() const {
return ~(occupied_squares()); return ~occupied_squares();
} }
inline Bitboard Position::pieces_of_color(Color c) const { inline Bitboard Position::pieces_of_color(Color c) const {
@@ -536,12 +535,10 @@ inline bool Position::move_is_passed_pawn_push(Move m) const {
} }
inline int Position::rule_50_counter() const { inline int Position::rule_50_counter() const {
return st->rule50; return st->rule50;
} }
inline int Position::startpos_ply_counter() const { inline int Position::startpos_ply_counter() const {
return startPosPlyCounter; return startPosPlyCounter;
} }
@@ -553,12 +550,10 @@ inline bool Position::opposite_colored_bishops() const {
} }
inline bool Position::has_pawn_on_7th(Color c) const { inline bool Position::has_pawn_on_7th(Color c) const {
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7); return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
} }
inline bool Position::is_chess960() const { inline bool Position::is_chess960() const {
return isChess960; return isChess960;
} }
@@ -582,4 +577,12 @@ inline int Position::thread() const {
return threadID; return threadID;
} }
inline void Position::do_allow_oo(Color c) {
st->castleRights |= (1 + int(c));
}
inline void Position::do_allow_ooo(Color c) {
st->castleRights |= (4 + 4*int(c));
}
#endif // !defined(POSITION_H_INCLUDED) #endif // !defined(POSITION_H_INCLUDED)

View File

@@ -27,18 +27,25 @@
#include "value.h" #include "value.h"
namespace {
//// ////
//// Constants modified by Joona Kiiski //// Constants modified by Joona Kiiski
//// ////
static const Value MP = PawnValueMidgame; const Value MP = PawnValueMidgame;
static const Value MK = KnightValueMidgame; const Value MK = KnightValueMidgame;
static const Value MB = BishopValueMidgame; const Value MB = BishopValueMidgame;
static const Value MR = RookValueMidgame; const Value MR = RookValueMidgame;
static const Value MQ = QueenValueMidgame; const Value MQ = QueenValueMidgame;
static const int MgPST[][64] = { const Value EP = PawnValueEndgame;
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 // A B C D E F G H
@@ -108,13 +115,7 @@ static const int MgPST[][64] = {
} }
}; };
static const Value EP = PawnValueEndgame; const int EgPST[][64] = {
static const Value EK = KnightValueEndgame;
static const Value EB = BishopValueEndgame;
static const Value ER = RookValueEndgame;
static const Value EQ = QueenValueEndgame;
static const int EgPST[][64] = {
{ }, { },
{// Pawn {// Pawn
// A B C D E F G H // A B C D E F G H
@@ -184,5 +185,6 @@ static const int EgPST[][64] = {
} }
}; };
} // namespace
#endif // !defined(PSQTAB_H_INCLUDED) #endif // !defined(PSQTAB_H_INCLUDED)

84
src/rkiss.h Normal file
View File

@@ -0,0 +1,84 @@
/*
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/>.
This file is based on original code by Heinz van Saanen and is
available under the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
** A small "keep it simple and stupid" RNG with some fancy merits:
**
** 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
**
** (c) Heinz van Saanen
*/
#if !defined(RKISS_H_INCLUDED)
#define RKISS_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Types
////
class RKISS {
// Keep variables always together
struct S { uint64_t a, b, c, d; } s;
// Return 64 bit unsigned integer in between [0,2^64-1]
uint64_t rand64() {
const uint64_t
e = s.a - ((s.b << 7) | (s.b >> 57));
s.a = s.b ^ ((s.c << 13) | (s.c >> 51));
s.b = s.c + ((s.d << 37) | (s.d >> 27));
s.c = s.d + e;
return s.d = e + s.a;
}
// Init seed and scramble a few rounds
void raninit() {
s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++)
rand64();
}
public:
RKISS() { raninit(); }
template<typename T> T rand() { return T(rand64()); }
};
#endif // !defined(RKISS_H_INCLUDED)

View File

@@ -28,8 +28,7 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include "history.h" #include "movegen.h"
#include "movepick.h"
#include "san.h" #include "san.h"
using std::string; using std::string;
@@ -41,14 +40,9 @@ using std::string;
namespace { namespace {
enum Ambiguity { enum Ambiguity {
AMBIGUITY_NONE, AMBIGUITY_NONE, AMBIGUITY_FILE, AMBIGUITY_RANK, AMBIGUITY_BOTH
AMBIGUITY_FILE,
AMBIGUITY_RANK,
AMBIGUITY_BOTH
}; };
const History H; // Used as dummy argument for MovePicker c'tor
Ambiguity move_ambiguity(const Position& pos, Move m); Ambiguity move_ambiguity(const Position& pos, Move m);
const string time_string(int milliseconds); const string time_string(int milliseconds);
const string score_string(Value v); const string score_string(Value v);
@@ -71,21 +65,23 @@ const string move_to_san(Position& pos, Move m) {
string san; string san;
Square from = move_from(m); Square from = move_from(m);
Square to = move_to(m); Square to = move_to(m);
PieceType pt = type_of_piece(pos.piece_on(move_from(m))); PieceType pt = type_of_piece(pos.piece_on(from));
if (m == MOVE_NONE) if (m == MOVE_NONE)
return "(none)"; return "(none)";
else if (m == MOVE_NULL)
if (m == MOVE_NULL)
return "(null)"; return "(null)";
else if (move_is_long_castle(m) || (int(to - from) == -2 && pt == KING))
if (move_is_long_castle(m))
san = "O-O-O"; san = "O-O-O";
else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING)) else if (move_is_short_castle(m))
san = "O-O"; san = "O-O";
else else
{ {
if (pt != PAWN) if (pt != PAWN)
{ {
san += piece_type_to_char(pt, true); san += piece_type_to_char(pt);
switch (move_ambiguity(pos, m)) { switch (move_ambiguity(pos, m)) {
case AMBIGUITY_NONE: case AMBIGUITY_NONE:
@@ -103,17 +99,20 @@ const string move_to_san(Position& pos, Move m) {
assert(false); assert(false);
} }
} }
if (pos.move_is_capture(m)) if (pos.move_is_capture(m))
{ {
if (pt == PAWN) if (pt == PAWN)
san += file_to_char(square_file(move_from(m))); san += file_to_char(square_file(from));
san += "x";
san += 'x';
} }
san += square_to_string(move_to(m)); san += square_to_string(to);
if (move_is_promotion(m)) if (move_is_promotion(m))
{ {
san += "="; san += '=';
san += piece_type_to_char(move_promotion_piece(m), true); san += piece_type_to_char(move_promotion_piece(m));
} }
} }
@@ -138,41 +137,43 @@ Move move_from_san(const Position& pos, const string& movestr) {
assert(pos.is_ok()); assert(pos.is_ok());
MovePicker mp = MovePicker(pos, MOVE_NONE, ONE_PLY, H); enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); static const string pieceLetters = "KQRBN";
MoveStack mlist[MOVES_MAX], *last;
PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE;
File fromFile = FILE_NONE, toFile = FILE_NONE;
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
Move move = MOVE_NONE;
Square from, to;
int matches, state = START;
// Generate all legal moves for the given position
last = generate_moves(pos, mlist);
// Castling moves // Castling moves
if (movestr == "O-O-O" || movestr == "O-O-O+") if (movestr == "O-O-O" || movestr == "O-O-O+")
{ {
Move m; for (MoveStack* cur = mlist; cur != last; cur++)
while ((m = mp.get_next_move()) != MOVE_NONE) if (move_is_long_castle(cur->move))
if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned)) return cur->move;
return m;
return MOVE_NONE; return MOVE_NONE;
} }
else if (movestr == "O-O" || movestr == "O-O+") else if (movestr == "O-O" || movestr == "O-O+")
{ {
Move m; for (MoveStack* cur = mlist; cur != last; cur++)
while ((m = mp.get_next_move()) != MOVE_NONE) if (move_is_short_castle(cur->move))
if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned)) return cur->move;
return m;
return MOVE_NONE; return MOVE_NONE;
} }
// Normal moves. We use a simple FSM to parse the san string. // Normal moves. We use a simple FSM to parse the san string
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
static const string pieceLetters = "KQRBN";
PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE;
File fromFile = FILE_NONE, toFile = FILE_NONE;
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
Square to;
int state = START;
for (size_t i = 0; i < movestr.length(); i++) for (size_t i = 0; i < movestr.length(); i++)
{ {
char type, c = movestr[i]; char type, c = movestr[i];
if (pieceLetters.find(c) != string::npos) if (pieceLetters.find(c) != string::npos)
type = 'P'; type = 'P';
else if (c >= 'a' && c <= 'h') else if (c >= 'a' && c <= 'h')
@@ -192,7 +193,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
else if (state == PROMOTION) else if (state == PROMOTION)
{ {
promotion = piece_type_from_char(c); promotion = piece_type_from_char(c);
state = (i < movestr.length() - 1) ? CHECK : END; state = (i < movestr.length() - 1 ? CHECK : END);
} }
else else
return MOVE_NONE; return MOVE_NONE;
@@ -232,7 +233,8 @@ Move move_from_san(const Position& pos, const string& movestr) {
else else
return MOVE_NONE; return MOVE_NONE;
break; break;
case 'x': case 'X': case 'x':
case 'X':
if (state == TO_RANK) if (state == TO_RANK)
{ {
// Previous file was for disambiguation, or it's a pawn capture // Previous file was for disambiguation, or it's a pawn capture
@@ -248,7 +250,8 @@ Move move_from_san(const Position& pos, const string& movestr) {
else else
return MOVE_NONE; return MOVE_NONE;
break; break;
case '+': case '#': case '+':
case '#':
if (state == PROMOTION_OR_CHECK || state == CHECK) if (state == PROMOTION_OR_CHECK || state == CHECK)
state = END; state = END;
else else
@@ -263,22 +266,25 @@ Move move_from_san(const Position& pos, const string& movestr) {
if (state != END) if (state != END)
return MOVE_NONE; return MOVE_NONE;
// Look for a matching move // Look for an unambiguous matching move
Move m, move = MOVE_NONE;
to = make_square(toFile, toRank); to = make_square(toFile, toRank);
int matches = 0; matches = 0;
while ((m = mp.get_next_move()) != MOVE_NONE) for (MoveStack* cur = mlist; cur != last; cur++)
if ( pos.type_of_piece_on(move_from(m)) == pt {
&& move_to(m) == to from = move_from(cur->move);
&& move_promotion_piece(m) == promotion
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m))) if ( pos.type_of_piece_on(from) == pt
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m)))) && move_to(cur->move) == to
&& move_promotion_piece(cur->move) == promotion
&& (fromFile == FILE_NONE || fromFile == square_file(from))
&& (fromRank == RANK_NONE || fromRank == square_rank(from)))
{ {
move = m; move = cur->move;
matches++; matches++;
} }
return (matches == 1 ? move : MOVE_NONE); }
return matches == 1 ? move : MOVE_NONE;
} }
@@ -322,11 +328,11 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
/// It is used to write search information to the log file (which is created /// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true"). /// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, const string pretty_pv(const Position& pos, int time, int depth,
Value score, ValueType type, Move pv[]) { Value score, ValueType type, Move pv[]) {
const uint64_t K = 1000; const int64_t K = 1000;
const uint64_t M = 1000000; const int64_t M = 1000000;
std::stringstream s; std::stringstream s;
@@ -341,12 +347,12 @@ const string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes,
s << std::setw(8) << time_string(time) << " "; s << std::setw(8) << time_string(time) << " ";
// Nodes // Nodes
if (nodes < M) if (pos.nodes_searched() < M)
s << std::setw(8) << nodes / 1 << " "; s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (nodes < K * M) else if (pos.nodes_searched() < K * M)
s << std::setw(7) << nodes / K << "K "; s << std::setw(7) << pos.nodes_searched() / K << "K ";
else else
s << std::setw(7) << nodes / M << "M "; s << std::setw(7) << pos.nodes_searched() / M << "M ";
// PV // PV
s << line_to_san(pos, pv, 30, true); s << line_to_san(pos, pv, 30, true);
@@ -359,43 +365,36 @@ namespace {
Ambiguity move_ambiguity(const Position& pos, Move m) { Ambiguity move_ambiguity(const Position& pos, Move m) {
MoveStack mlist[MOVES_MAX], *last;
Move candidates[8];
Square from = move_from(m); Square from = move_from(m);
Square to = move_to(m); Square to = move_to(m);
Piece pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
int matches = 0, f = 0, r = 0;
// King moves are never ambiguous, because there is never two kings of // If there is only one piece 'pc' then move cannot be ambiguous
// the same color. if (pos.piece_count(pos.side_to_move(), type_of_piece(pc)) == 1)
if (type_of_piece(pc) == KING)
return AMBIGUITY_NONE; return AMBIGUITY_NONE;
MovePicker mp = MovePicker(pos, MOVE_NONE, ONE_PLY, H); // Collect all legal moves of piece 'pc' with destination 'to'
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); last = generate_moves(pos, mlist);
Move mv, moveList[8]; for (MoveStack* cur = mlist; cur != last; cur++)
if (move_to(cur->move) == to && pos.piece_on(move_from(cur->move)) == pc)
candidates[matches++] = cur->move;
int n = 0; if (matches == 1)
while ((mv = mp.get_next_move()) != MOVE_NONE)
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned))
moveList[n++] = mv;
if (n == 1)
return AMBIGUITY_NONE; return AMBIGUITY_NONE;
int f = 0, r = 0; for (int i = 0; i < matches; i++)
for (int i = 0; i < n; i++)
{ {
if (square_file(move_from(moveList[i])) == square_file(from)) if (square_file(move_from(candidates[i])) == square_file(from))
f++; f++;
if (square_rank(move_from(moveList[i])) == square_rank(from)) if (square_rank(move_from(candidates[i])) == square_rank(from))
r++; r++;
} }
if (f == 1)
return AMBIGUITY_FILE;
if (r == 1) return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH;
return AMBIGUITY_RANK;
return AMBIGUITY_BOTH;
} }

View File

@@ -39,6 +39,6 @@
extern const std::string move_to_san(Position& pos, Move m); extern const std::string move_to_san(Position& pos, Move m);
extern Move move_from_san(const Position& pos, const std::string& str); extern Move move_from_san(const Position& pos, const std::string& str);
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines); extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, ValueType type, Move pv[]); extern const std::string pretty_pv(const Position& pos, int time, int depth, Value score, ValueType type, Move pv[]);
#endif // !defined(SAN_H_INCLUDED) #endif // !defined(SAN_H_INCLUDED)

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,7 @@
//// ////
const int PLY_MAX = 100; const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = 102; const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
//// ////
@@ -47,6 +47,7 @@ const int PLY_MAX_PLUS_2 = 102;
/// search thread has its own array of SearchStack objects, indexed by the /// search thread has its own array of SearchStack objects, indexed by the
/// current ply. /// current ply.
struct EvalInfo; struct EvalInfo;
struct SplitPoint;
struct SearchStack { struct SearchStack {
Move currentMove; Move currentMove;
@@ -56,7 +57,9 @@ struct SearchStack {
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value eval; Value eval;
Value evalMargin;
bool skipNullMove; bool skipNullMove;
SplitPoint* sp;
}; };
@@ -68,8 +71,7 @@ extern void init_search();
extern void init_threads(); extern void init_threads();
extern void exit_threads(); extern void exit_threads();
extern int perft(Position& pos, Depth depth); extern int perft(Position& pos, Depth depth);
extern int64_t nodes_searched(); extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
extern bool think(const Position& pos, bool infinite, bool ponder, int time[], int increment[],
int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]); int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)

View File

@@ -57,11 +57,15 @@ enum Rank {
}; };
enum SquareDelta { enum SquareDelta {
DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
DELTA_SW = -011, DELTA_S = -010, DELTA_SE = -07, DELTA_SEE = -06, DELTA_N = 8, DELTA_E = 1, DELTA_S = -8, DELTA_W = -1, DELTA_NONE = 0,
DELTA_W = -01, DELTA_ZERO = 0, DELTA_E = 01, DELTA_NWW = 06, DELTA_NW = 07,
DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017, DELTA_NN = DELTA_N + DELTA_N,
DELTA_NN = 020, DELTA_NNE = 021 DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
}; };
ENABLE_OPERATORS_ON(Square); ENABLE_OPERATORS_ON(Square);
@@ -74,9 +78,8 @@ ENABLE_OPERATORS_ON(SquareDelta);
//// Constants //// Constants
//// ////
const int FlipMask = 070; const int FlipMask = 56;
const int FlopMask = 07; const int FlopMask = 7;
//// ////
//// Inline functions //// Inline functions
@@ -160,10 +163,6 @@ inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1')); return char(r - RANK_1 + int('1'));
} }
inline Square square_from_string(const std::string& str) {
return make_square(file_from_char(str[0]), rank_from_char(str[1]));
}
inline const std::string square_to_string(Square s) { inline const std::string square_to_string(Square s) {
return std::string(1, file_to_char(square_file(s))) return std::string(1, file_to_char(square_file(s)))
+ std::string(1, rank_to_char(square_rank(s))); + std::string(1, rank_to_char(square_rank(s)));

View File

@@ -38,7 +38,7 @@
//// Constants and variables //// Constants and variables
//// ////
const int MAX_THREADS = 8; const int MAX_THREADS = 16;
const int MAX_ACTIVE_SPLIT_POINTS = 8; const int MAX_ACTIVE_SPLIT_POINTS = 8;
@@ -55,6 +55,7 @@ struct SplitPoint {
bool pvNode, mateThreat; bool pvNode, mateThreat;
Value beta; Value beta;
int ply; int ply;
int master;
Move threatMove; Move threatMove;
SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2]; SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2];
@@ -64,10 +65,11 @@ struct SplitPoint {
// Shared data // Shared data
Lock lock; Lock lock;
volatile int64_t nodes;
volatile Value alpha; volatile Value alpha;
volatile Value bestValue; volatile Value bestValue;
volatile int moveCount; volatile int moveCount;
volatile bool stopRequest; volatile bool betaCutoff;
volatile int slaves[MAX_THREADS]; volatile int slaves[MAX_THREADS];
}; };
@@ -75,16 +77,15 @@ struct SplitPoint {
enum ThreadState enum ThreadState
{ {
THREAD_INITIALIZING, // thread is initializing itself
THREAD_SEARCHING, // thread is performing work THREAD_SEARCHING, // thread is performing work
THREAD_AVAILABLE, // thread is polling for work THREAD_AVAILABLE, // thread is waiting for work
THREAD_SLEEPING, // we are not thinking, so thread is sleeping
THREAD_BOOKED, // other thread (master) has booked us as a slave THREAD_BOOKED, // other thread (master) has booked us as a slave
THREAD_WORKISWAITING, // master has ordered us to start THREAD_WORKISWAITING, // master has ordered us to start
THREAD_TERMINATED // we are quitting and thread is terminated THREAD_TERMINATED // we are quitting and thread is terminated
}; };
struct Thread { struct Thread {
uint64_t nodes;
volatile ThreadState state; volatile ThreadState state;
SplitPoint* volatile splitPoint; SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;

View File

@@ -88,7 +88,7 @@ namespace {
//// Functions //// Functions
//// ////
void TimeManager::pv_unstability(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);
@@ -114,10 +114,10 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
int hypMTG, hypMyTime, t1, t2; int hypMTG, hypMyTime, t1, t2;
// Read uci parameters // Read uci parameters
int emergencyMoveHorizon = get_option_value_int("Emergency Move Horizon"); int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>();
int emergencyBaseTime = get_option_value_int("Emergency Base Time"); int emergencyBaseTime = Options["Emergency Base Time"].value<int>();
int emergencyMoveTime = get_option_value_int("Emergency Move Time"); int emergencyMoveTime = Options["Emergency Move Time"].value<int>();
int minThinkingTime = get_option_value_int("Minimum Thinking Time"); int minThinkingTime = Options["Minimum Thinking Time"].value<int>();
// Initialize to maximum values but unstablePVExtraTime that is reset // Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0; unstablePVExtraTime = 0;
@@ -137,7 +137,7 @@ void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
maximumSearchTime = Min(maximumSearchTime, t2); maximumSearchTime = Min(maximumSearchTime, t2);
} }
if (get_option_value_bool("Ponder")) if (Options["Ponder"].value<bool>())
optimumSearchTime += optimumSearchTime / 4; optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime // Make sure that maxSearchTime is not over absoluteMaxSearchTime

View File

@@ -29,7 +29,7 @@ class TimeManager {
public: public:
void init(int myTime, int myInc, int movesToGo, int currentPly); void init(int myTime, int myInc, int movesToGo, int currentPly);
void pv_unstability(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; }

View File

@@ -70,8 +70,9 @@ void TranspositionTable::set_size(size_t mbSize) {
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl; << " MB for transposition table." << std::endl;
Application::exit_with_failure(); exit(EXIT_FAILURE);
} }
clear();
} }
} }

View File

@@ -64,6 +64,7 @@ public:
staticValue = int16_t(statV); staticValue = int16_t(statV);
staticValueMargin = int16_t(kd); staticValueMargin = int16_t(kd);
} }
void set_generation(int g) { data = move() | (type() << 21) | (g << 23); }
uint32_t key() const { return key32; } uint32_t key() const { return key32; }
Depth depth() const { return Depth(depth16); } Depth depth() const { return Depth(depth16); }
@@ -102,6 +103,9 @@ struct TTCluster {
class TranspositionTable { class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
public: public:
TranspositionTable(); TranspositionTable();
~TranspositionTable(); ~TranspositionTable();
@@ -111,11 +115,12 @@ public:
TTEntry* retrieve(const Key posKey) const; TTEntry* retrieve(const Key posKey) const;
void new_search(); void new_search();
TTEntry* first_entry(const Key posKey) const; TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
private: private:
size_t size; size_t size;
TTCluster* entries; TTCluster* entries;
uint8_t generation; uint8_t generation; // To properly compare, size must be smaller then TT stored value
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
@@ -130,4 +135,13 @@ inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[uint32_t(posKey) & (size - 1)].data; return entries[uint32_t(posKey) & (size - 1)].data;
} }
/// TranspositionTable::refresh updates the 'generation' value of the TTEntry
/// to avoid aging. Normally called after a TT hit, before to return.
inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
#endif // !defined(TT_H_INCLUDED) #endif // !defined(TT_H_INCLUDED)

View File

@@ -17,7 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TYPES_H_INCLUDED) #if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
@@ -27,6 +26,10 @@
#else #else
// 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
typedef __int8 int8_t; typedef __int8 int8_t;
typedef unsigned __int8 uint8_t; typedef unsigned __int8 uint8_t;
typedef __int16 int16; typedef __int16 int16;
@@ -47,6 +50,7 @@ typedef uint64_t Key;
// Bitboard type // Bitboard type
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
#include <cstdlib>
//// ////
//// Configuration //// Configuration
@@ -150,10 +154,10 @@ template<typename T>
inline T operator- (const T d) { OK(T); return T(-int(d)); } inline T operator- (const T d) { OK(T); return T(-int(d)); }
template<typename T> template<typename T>
inline void operator++ (T& d, int) { OK(T); d = T(int(d) + 1); } inline T operator++ (T& d, int) { OK(T); d = T(int(d) + 1); return d; }
template<typename T> template<typename T>
inline void operator-- (T& d, int) { OK(T); d = T(int(d) - 1); } inline T operator-- (T& d, int) { OK(T); d = T(int(d) - 1); return d; }
template<typename T> template<typename T>
inline void operator+= (T& d1, const T d2) { OK(T); d1 = d1 + d2; } inline void operator+= (T& d1, const T d2) { OK(T); d1 = d1 + d2; }

View File

@@ -27,7 +27,6 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "move.h" #include "move.h"
@@ -35,62 +34,95 @@
#include "position.h" #include "position.h"
#include "san.h" #include "san.h"
#include "search.h" #include "search.h"
#include "uci.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
////
//// Local definitions:
////
namespace { namespace {
// FEN string for the initial position // FEN string for the initial position
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// UCIInputParser is a class for parsing UCI input. The class // UCIParser is a class for parsing UCI input. The class
// is actually a string stream built on a given input string. // is actually a string stream built on a given input string.
typedef istringstream UCIParser;
typedef istringstream UCIInputParser;
// The root position. This is set up when the user (or in practice, the GUI)
// sends the "position" UCI command. The root position is sent to the think()
// function when the program receives the "go" command.
Position RootPosition(0);
// Local functions // Local functions
bool handle_command(const string& command); void set_option(UCIParser& uip);
void set_option(UCIInputParser& uip); void set_position(Position& pos, UCIParser& uip);
void set_position(UCIInputParser& uip); bool go(Position& pos, UCIParser& uip);
bool go(UCIInputParser& uip); void perft(Position& pos, UCIParser& uip);
void perft(UCIInputParser& uip);
} }
//// /// execute_uci_command() takes a string as input, uses a UCIParser
//// Functions /// object to parse this text string as a UCI command, and calls
//// /// the appropriate functions. In addition to the UCI commands,
/// the function also supports a few debug commands.
/// uci_main_loop() is the only global function in this file. It is bool execute_uci_command(const string& cmd) {
/// called immediately after the program has finished initializing.
/// The program remains in this loop until it receives the "quit" UCI
/// command. It waits for a command from the user, and passes this
/// command to handle_command and also intercepts EOF from stdin,
/// by translating EOF to the "quit" command. This ensures that Stockfish
/// exits gracefully if the GUI dies unexpectedly.
void uci_main_loop() { static Position pos(StartPositionFEN, 0); // The root position
UCIParser up(cmd);
string token;
RootPosition.from_fen(StartPositionFEN); if (!(up >> token)) // operator>>() skips any whitespace
string command; return true;
do { if (token == "quit")
// Wait for a command from stdin return false;
if (!getline(cin, command))
command = "quit";
} while (handle_command(command)); if (token == "go")
return go(pos, up);
if (token == "uci")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame")
pos.from_fen(StartPositionFEN);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, up);
else if (token == "setoption")
set_option(up);
// The remaining commands are for debugging purposes only
else if (token == "d")
pos.print();
else if (token == "flip")
{
Position p(pos, pos.thread());
pos.flipped_copy(p);
}
else if (token == "eval")
{
Value evalMargin;
cout << "Incremental mg: " << mg_value(pos.value())
<< "\nIncremental eg: " << eg_value(pos.value())
<< "\nFull eval: " << evaluate(pos, evalMargin) << endl;
}
else if (token == "key")
cout << "key: " << hex << pos.get_key()
<< "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "perft")
perft(pos, up);
else
cout << "Unknown command: " << cmd << endl;
return true;
} }
@@ -100,158 +132,103 @@ void uci_main_loop() {
namespace { namespace {
// handle_command() takes a text string as input, uses a
// UCIInputParser object to parse this text string as a UCI command,
// and calls the appropriate functions. In addition to the UCI
// commands, the function also supports a few debug commands.
bool handle_command(const string& command) {
UCIInputParser uip(command);
string token;
if (!(uip >> token)) // operator>>() skips any whitespace
return true;
if (token == "quit")
return false;
if (token == "go")
return go(uip);
if (token == "uci")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame")
{
push_button("New Game");
RootPosition.from_fen(StartPositionFEN);
}
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(uip);
else if (token == "setoption")
set_option(uip);
// The remaining commands are for debugging purposes only.
// Perhaps they should be removed later in order to reduce the
// size of the program binary.
else if (token == "d")
RootPosition.print();
else if (token == "flip")
{
Position p(RootPosition, RootPosition.thread());
RootPosition.flipped_copy(p);
}
else if (token == "eval")
{
Value evalMargin;
cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, evalMargin) << endl;
}
else if (token == "key")
cout << "key: " << hex << RootPosition.get_key()
<< "\nmaterial key: " << RootPosition.get_material_key()
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
else if (token == "perft")
perft(uip);
else
cout << "Unknown command: " << command << endl;
return true;
}
// set_position() is called when Stockfish receives the "position" UCI // set_position() is called when Stockfish receives the "position" UCI
// command. The input parameter is a UCIInputParser. It is assumed // command. The input parameter is a UCIParser. It is assumed
// that this parser has consumed the first token of the UCI command // that this parser has consumed the first token of the UCI command
// ("position"), and is ready to read the second token ("startpos" // ("position"), and is ready to read the second token ("startpos"
// or "fen", if the input is well-formed). // or "fen", if the input is well-formed).
void set_position(UCIInputParser& uip) { void set_position(Position& pos, UCIParser& up) {
string token; string token;
if (!(uip >> token)) // operator>>() skips any whitespace if (!(up >> token) || (token != "startpos" && token != "fen"))
return; return;
if (token == "startpos") if (token == "startpos")
RootPosition.from_fen(StartPositionFEN); {
else if (token == "fen") pos.from_fen(StartPositionFEN);
if (!(up >> token))
return;
}
else // fen
{ {
string fen; string fen;
while (uip >> token && token != "moves") while (up >> token && token != "moves")
{ {
fen += token; fen += token;
fen += ' '; fen += ' ';
} }
RootPosition.from_fen(fen); pos.from_fen(fen);
} }
if (uip.good()) if (token != "moves")
return;
// Parse optional move list
Move move;
StateInfo st;
while (up >> token)
{ {
if (token != "moves") move = move_from_uci(pos, token);
uip >> token; pos.do_move(move, st);
if (pos.rule_50_counter() == 0)
pos.reset_game_ply();
if (token == "moves") pos.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
{
Move move;
StateInfo st;
while (uip >> token)
{
move = move_from_string(RootPosition, token);
RootPosition.do_move(move, st);
if (RootPosition.rule_50_counter() == 0)
RootPosition.reset_game_ply();
RootPosition.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
}
// Our StateInfo st is about going out of scope so copy
// its content inside RootPosition before it disappears.
RootPosition.detach();
}
} }
// Our StateInfo st is about going out of scope so copy
// its content inside pos before it disappears.
pos.detach();
} }
// set_option() is called when Stockfish receives the "setoption" UCI // set_option() is called when Stockfish receives the "setoption" UCI
// command. The input parameter is a UCIInputParser. It is assumed // command. The input parameter is a UCIParser. It is assumed
// that this parser has consumed the first token of the UCI command // that this parser has consumed the first token of the UCI command
// ("setoption"), and is ready to read the second token ("name", if // ("setoption"), and is ready to read the second token ("name", if
// the input is well-formed). // the input is well-formed).
void set_option(UCIInputParser& uip) { void set_option(UCIParser& up) {
string token, name, value; string token, name, value;
if (!(uip >> token)) // operator>>() skips any whitespace if (!(up >> token) || token != "name") // operator>>() skips any whitespace
return; return;
if (token == "name" && uip >> name) if (!(up >> name))
return;
// Handle names with included spaces
while (up >> token && token != "value")
name += (" " + token);
if (Options.find(name) == Options.end())
{ {
while (uip >> token && token != "value") cout << "No such option: " << name << endl;
name += (" " + token); return;
if (token == "value" && uip >> value)
{
while (uip >> token)
value += (" " + token);
set_option_value(name, value);
} else
push_button(name);
} }
// Is a button ?
if (token != "value")
{
Options[name].set_value("true");
return;
}
if (!(up >> value))
return;
// Handle values with included spaces
while (up >> token)
value += (" " + token);
Options[name].set_value(value);
} }
// go() is called when Stockfish receives the "go" UCI command. The // go() is called when Stockfish receives the "go" UCI command. The
// input parameter is a UCIInputParser. It is assumed that this // input parameter is a UCIParser. It is assumed that this
// parser has consumed the first token of the UCI command ("go"), // parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the // and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and // thinking time and other parameters from the input string, and
@@ -259,62 +236,60 @@ namespace {
// parameters. Returns false if a quit command is received while // parameters. Returns false if a quit command is received while
// thinking, returns true otherwise. // thinking, returns true otherwise.
bool go(UCIInputParser& uip) { bool go(Position& pos, UCIParser& up) {
string token; string token;
int time[2] = {0, 0}, inc[2] = {0, 0}; int time[2] = {0, 0}, inc[2] = {0, 0};
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0; int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
bool infinite = false, ponder = false; bool infinite = false, ponder = false;
Move searchMoves[500]; Move searchMoves[MOVES_MAX];
searchMoves[0] = MOVE_NONE; searchMoves[0] = MOVE_NONE;
while (uip >> token) while (up >> token)
{ {
if (token == "infinite") if (token == "infinite")
infinite = true; infinite = true;
else if (token == "ponder") else if (token == "ponder")
ponder = true; ponder = true;
else if (token == "wtime") else if (token == "wtime")
uip >> time[0]; up >> time[0];
else if (token == "btime") else if (token == "btime")
uip >> time[1]; up >> time[1];
else if (token == "winc") else if (token == "winc")
uip >> inc[0]; up >> inc[0];
else if (token == "binc") else if (token == "binc")
uip >> inc[1]; up >> inc[1];
else if (token == "movestogo") else if (token == "movestogo")
uip >> movesToGo; up >> movesToGo;
else if (token == "depth") else if (token == "depth")
uip >> depth; up >> depth;
else if (token == "nodes") else if (token == "nodes")
uip >> nodes; up >> nodes;
else if (token == "movetime") else if (token == "movetime")
uip >> moveTime; up >> moveTime;
else if (token == "searchmoves") else if (token == "searchmoves")
{ {
int numOfMoves = 0; int numOfMoves = 0;
while (uip >> token) while (up >> token)
searchMoves[numOfMoves++] = move_from_string(RootPosition, token); searchMoves[numOfMoves++] = move_from_uci(pos, token);
searchMoves[numOfMoves] = MOVE_NONE; searchMoves[numOfMoves] = MOVE_NONE;
} }
} }
assert(RootPosition.is_ok()); assert(pos.is_ok());
return think(RootPosition, infinite, ponder, time, inc, movesToGo, return think(pos, infinite, ponder, time, inc, movesToGo,
depth, nodes, moveTime, searchMoves); depth, nodes, moveTime, searchMoves);
} }
void perft(UCIInputParser& uip) { void perft(Position& pos, UCIParser& up) {
string token;
int depth, tm, n; int depth, tm, n;
Position pos(RootPosition, RootPosition.thread());
if (!(uip >> depth)) if (!(up >> depth))
return; return;
tm = get_system_time(); tm = get_system_time();
@@ -324,6 +299,6 @@ namespace {
tm = get_system_time() - tm; tm = get_system_time() - tm;
std::cout << "\nNodes " << n std::cout << "\nNodes " << n
<< "\nTime (ms) " << tm << "\nTime (ms) " << tm
<< "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl; << "\nNodes/second " << int(n / (tm / 1000.0)) << std::endl;
} }
} }

View File

@@ -1,31 +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(UCI_H_INCLUDED)
#define UCI_H_INCLUDED
////
//// Prototypes
////
extern void uci_main_loop();
#endif // !defined(UCI_H_INCLUDED)

View File

@@ -17,17 +17,9 @@
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 <iostream>
//// Includes
////
#include <algorithm>
#include <cassert>
#include <map>
#include <string>
#include <sstream> #include <sstream>
#include <vector>
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
@@ -37,150 +29,87 @@ using std::string;
using std::cout; using std::cout;
using std::endl; using std::endl;
//// OptionsMap Options; // Global object
//// Local definitions
////
namespace {
enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON }; // Our case insensitive less() function as required by UCI protocol
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
typedef std::vector<string> StrVector; int c1, c2;
size_t i = 0;
struct Option { while (i < s1.size() && i < s2.size())
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
string name, defaultValue, currentValue; if (c1 != c2)
OptionType type; return c1 < c2;
size_t idx;
int minValue, maxValue;
StrVector comboValues;
Option();
Option(const char* defaultValue, OptionType = STRING);
Option(bool defaultValue, OptionType = CHECK);
Option(int defaultValue, int minValue, int maxValue);
bool operator<(const Option& o) const { return idx < o.idx; }
};
typedef std::vector<Option> OptionsVector;
typedef std::map<string, Option> Options;
Options options;
// stringify() converts a value of type T to a std::string
template<typename T>
string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
} }
return s1.size() < s2.size();
Option::Option() {} // To allow insertion in a std::map
Option::Option(const char* def, OptionType t)
: defaultValue(def), currentValue(def), type(t), idx(options.size()), minValue(0), maxValue(0) {}
Option::Option(bool def, OptionType t)
: defaultValue(stringify(def)), currentValue(stringify(def)), type(t), idx(options.size()), minValue(0), maxValue(0) {}
Option::Option(int def, int minv, int maxv)
: defaultValue(stringify(def)), currentValue(stringify(def)), type(SPIN), idx(options.size()), minValue(minv), maxValue(maxv) {}
// load_defaults() populates the options map with the hard
// coded names and default values.
void load_defaults(Options& o) {
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);
o["Mobility (Endgame)"] = Option(100, 0, 200);
o["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200);
o["Space"] = Option(100, 0, 200);
o["Aggressiveness"] = Option(100, 0, 200);
o["Cowardice"] = Option(100, 0, 200);
o["Check Extension (PV nodes)"] = Option(2, 0, 2);
o["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
o["Single Evasion Extension (PV nodes)"] = Option(2, 0, 2);
o["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
o["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
o["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
o["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
o["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
o["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Minimum Split Depth"] = Option(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
o["Threads"] = Option(1, 1, MAX_THREADS);
o["Hash"] = Option(32, 4, 8192);
o["Clear Hash"] = Option(false, BUTTON);
o["New Game"] = Option(false, BUTTON);
o["Ponder"] = Option(true);
o["OwnBook"] = Option(true);
o["MultiPV"] = Option(1, 1, 500);
o["Emergency Move Horizon"] = Option(40, 0, 50);
o["Emergency Base Time"] = Option(200, 0, 60000);
o["Emergency Move Time"] = Option(70, 0, 5000);
o["Minimum Thinking Time"] = Option(20, 0, 5000);
o["UCI_Chess960"] = Option(false); // Just a dummy but needed by GUIs
o["UCI_AnalyseMode"] = Option(false);
// Any option should know its name so to be easily printed
for (Options::iterator it = o.begin(); it != o.end(); ++it)
it->second.name = it->first;
}
// get_option_value() implements the various get_option_value_<type>
// functions defined later.
template<typename T>
T get_option_value(const string& optionName) {
T ret = T();
if (options.find(optionName) == options.end())
return ret;
std::istringstream ss(options[optionName].currentValue);
ss >> ret;
return ret;
}
// Specialization for std::string where instruction 'ss >> ret'
// would erroneusly tokenize a string with spaces.
template<>
string get_option_value<string>(const string& optionName) {
if (options.find(optionName) == options.end())
return string();
return options[optionName].currentValue;
}
} }
/// init_uci_options() initializes the UCI options. Currently, the only thing // stringify() converts a numeric value of type T to a std::string
/// this function does is to initialize the default value of "Threads" and template<typename T>
/// "Minimum Split Depth" parameters according to the number of CPU cores. static string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
}
/// init_uci_options() initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
void init_uci_options() { void init_uci_options() {
load_defaults(options); Options["Use Search Log"] = Option(false);
Options["Search Log Filename"] = Option("SearchLog.txt");
Options["Book File"] = Option("book.bin");
Options["Best Book Move"] = Option(false);
Options["Mobility (Middle Game)"] = Option(100, 0, 200);
Options["Mobility (Endgame)"] = Option(100, 0, 200);
Options["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
Options["Pawn Structure (Endgame)"] = Option(100, 0, 200);
Options["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
Options["Passed Pawns (Endgame)"] = Option(100, 0, 200);
Options["Space"] = Option(100, 0, 200);
Options["Aggressiveness"] = Option(100, 0, 200);
Options["Cowardice"] = Option(100, 0, 200);
Options["Check Extension (PV nodes)"] = Option(2, 0, 2);
Options["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
Options["Single Evasion Extension (PV nodes)"] = Option(2, 0, 2);
Options["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
Options["Mate Threat Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
Options["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
Options["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
Options["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
Options["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
Options["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
Options["Minimum Split Depth"] = Option(4, 4, 7);
Options["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
Options["Threads"] = Option(1, 1, MAX_THREADS);
Options["Use Sleeping Threads"] = Option(false);
Options["Hash"] = Option(32, 4, 8192);
Options["Clear Hash"] = Option(false, "button");
Options["Ponder"] = Option(true);
Options["OwnBook"] = Option(true);
Options["MultiPV"] = Option(1, 1, 500);
Options["Emergency Move Horizon"] = Option(40, 0, 50);
Options["Emergency Base Time"] = Option(200, 0, 60000);
Options["Emergency Move Time"] = Option(70, 0, 5000);
Options["Minimum Thinking Time"] = Option(20, 0, 5000);
Options["UCI_Chess960"] = Option(false); // Just a dummy but needed by GUIs
Options["UCI_AnalyseMode"] = Option(false);
assert(options.find("Threads") != options.end()); // Set some SMP parameters accordingly to the detected CPU count
assert(options.find("Minimum Split Depth") != options.end()); Option& thr = Options["Threads"];
Option& msd = Options["Minimum Split Depth"];
Option& thr = options["Threads"];
Option& msd = options["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count()); thr.defaultValue = thr.currentValue = stringify(cpu_count());
@@ -190,130 +119,60 @@ void init_uci_options() {
/// print_uci_options() prints all the UCI options to the standard output, /// print_uci_options() prints all the UCI options to the standard output,
/// in the format defined by the UCI protocol. /// in chronological insertion order (the idx field) and in the format
/// defined by the UCI protocol.
void print_uci_options() { void print_uci_options() {
const char OptTypeName[][16] = { for (size_t i = 0; i <= Options.size(); i++)
"spin", "combo", "check", "string", "button" for (OptionsMap::const_iterator it = Options.begin(); it != Options.end(); ++it)
}; if (it->second.idx == i)
{
const Option& o = it->second;
cout << "\noption name " << it->first << " type " << o.type;
// Build up a vector out of the options map and sort it according to idx if (o.type != "button")
// field, that is the chronological insertion order in options map. cout << " default " << o.defaultValue;
OptionsVector vec;
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
vec.push_back(it->second);
std::sort(vec.begin(), vec.end()); if (o.type == "spin")
cout << " min " << o.minValue << " max " << o.maxValue;
for (OptionsVector::const_iterator it = vec.begin(); it != vec.end(); ++it) break;
{ }
cout << "\noption name " << it->name << " type " << OptTypeName[it->type];
if (it->type == BUTTON)
continue;
if (it->type == CHECK)
cout << " default " << (it->defaultValue == "1" ? "true" : "false");
else
cout << " default " << it->defaultValue;
if (it->type == SPIN)
cout << " min " << it->minValue << " max " << it->maxValue;
else if (it->type == COMBO)
{
StrVector::const_iterator itc;
for (itc = it->comboValues.begin(); itc != it->comboValues.end(); ++itc)
cout << " var " << *itc;
}
}
cout << endl; cout << endl;
} }
/// get_option_value_bool() returns the current value of a UCI parameter of /// Option class c'tors
/// type "check".
bool get_option_value_bool(const string& optionName) { Option::Option(const char* def) : type("string"), idx(Options.size()), minValue(0), maxValue(0)
{ defaultValue = currentValue = def; }
return get_option_value<bool>(optionName); Option::Option(bool def, string t) : type(t), idx(Options.size()), minValue(0), maxValue(0)
} { defaultValue = currentValue = (def ? "true" : "false"); }
Option::Option(int def, int minv, int maxv) : type("spin"), idx(Options.size()), minValue(minv), maxValue(maxv)
{ defaultValue = currentValue = stringify(def); }
/// get_option_value_int() returns the value of a UCI parameter as an integer. /// set_value() updates currentValue of the Option object. Normally it's up to
/// Normally, this function will be used for a parameter of type "spin", but /// the GUI to check for option's limits, but we could receive the new value
/// it could also be used with a "combo" parameter, where all the available /// directly from the user by teminal window. So let's check the bounds anyway.
/// values are integers.
int get_option_value_int(const string& optionName) { void Option::set_value(const string& value) {
return get_option_value<int>(optionName); assert(!type.empty());
}
if ( (type == "check" || type == "button")
/// get_option_value_string() returns the current value of a UCI parameter as && !(value == "true" || value == "false"))
/// a string. It is used with parameters of type "combo" and "string".
string get_option_value_string(const string& optionName) {
return get_option_value<string>(optionName);
}
/// set_option_value() inserts a new value for a UCI parameter
void set_option_value(const string& name, const string& value) {
if (options.find(name) == options.end())
{
cout << "No such option: " << name << endl;
return;
}
// UCI protocol uses "true" and "false" instead of "1" and "0", so convert
// value according to standard C++ convention before to store it.
string v(value);
if (v == "true")
v = "1";
else if (v == "false")
v = "0";
// Normally it's up to the GUI to check for option's limits,
// but we could receive the new value directly from the user
// by teminal window. So let's check the bounds anyway.
Option& opt = options[name];
if (opt.type == CHECK && v != "0" && v != "1")
return; return;
else if (opt.type == SPIN) if (type == "spin")
{ {
int val = atoi(v.c_str()); int v = atoi(value.c_str());
if (val < opt.minValue || val > opt.maxValue) if (v < minValue || v > maxValue)
return; return;
} }
opt.currentValue = v;
} currentValue = value;
/// push_button() is used to tell the engine that a UCI parameter of type
/// "button" has been selected:
void push_button(const string& buttonName) {
set_option_value(buttonName, "true");
}
/// button_was_pressed() tests whether a UCI parameter of type "button" has
/// been selected since the last time the function was called, in this case
/// it also resets the button.
bool button_was_pressed(const string& buttonName) {
if (!get_option_value<bool>(buttonName))
return false;
set_option_value(buttonName, "false");
return true;
} }

View File

@@ -17,28 +17,64 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
//// #include <cassert>
//// Includes #include <cstdlib>
//// #include <map>
#include <string> #include <string>
//// class Option {
//// Prototypes public:
//// Option() {} // To allow insertion in a std::map
Option(const char* defaultValue);
Option(bool defaultValue, std::string type = "check");
Option(int defaultValue, int minValue, int maxValue);
void set_value(const std::string& value);
template<typename T> T value() const;
private:
friend void init_uci_options();
friend void print_uci_options();
std::string defaultValue, currentValue, type;
size_t idx;
int minValue, maxValue;
};
template<typename T>
inline T Option::value() const {
assert(type == "spin");
return T(atoi(currentValue.c_str()));
}
template<>
inline std::string Option::value<std::string>() const {
assert(type == "string");
return currentValue;
}
template<>
inline bool Option::value<bool>() const {
assert(type == "check" || type == "button");
return currentValue == "true";
}
// Custom comparator because UCI options should not be case sensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
extern OptionsMap Options;
extern void init_uci_options(); extern void init_uci_options();
extern void print_uci_options(); extern void print_uci_options();
extern bool get_option_value_bool(const std::string& optionName);
extern int get_option_value_int(const std::string& optionName);
extern std::string get_option_value_string(const std::string& optionName);
extern bool button_was_pressed(const std::string& buttonName);
extern void set_option_value(const std::string& optionName,const std::string& newValue);
extern void push_button(const std::string& buttonName);
#endif // !defined(UCIOPTION_H_INCLUDED) #endif // !defined(UCIOPTION_H_INCLUDED)