Compare commits

...

314 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
Marco Costalba 71e14bb67b Stockfish 1.9.1
Fix release to workaround chess960 on some GUIs

Signature is:

stockfish bench 128 1 12 default depth

Node counts: 10914593

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-05 12:00:24 +01:00
Marco Costalba 66a64406ca Fix broken chess960 under Shredder GUI
We need to add a dummy option anyway to make GUIs happy.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-05 12:00:16 +01:00
Marco Costalba d4876dc963 Rewrite bit counting functions
Get rid of macros and use templates instead,
this is safer and allows us fix the warning:

ISO C++ forbids braced-groups within expressions

That broke compilation with -pedantic flag under
gcc and POPCNT enabled.

No functional and no performance change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-04 18:40:44 +01:00
Marco Costalba 3249777cdb Remove -pedantic option
Breaks current POPCNT code.

Perhaps we will readd with a proper fix...

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-03 14:43:17 +01:00
Marco Costalba 544adf7e41 Use special handling for promotions in move_is_legal()
Simplifies a bit the code and the common case too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-03 11:15:53 +01:00
Marco Costalba 98b09b4038 Fix an obsoleted NO_PIECE_TYPE in a comment
Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-02 18:57:46 +01:00
Marco Costalba 0aeba002c8 Restore development version
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-02 14:36:43 +01:00
Marco Costalba 247c2cd207 Increase warning level
Both under gcc and icc: sf compiles with no warnings !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-02 14:32:43 +01:00
Marco Costalba 46c16ab783 Stockfish 1.9
Signature is:

stockfish bench 128 1 12 default depth

Node counts: 10914593

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-10-02 09:55:10 +01:00
Joona Kiiski 3fd0079807 Less aggressive move count based futility pruning
This patch from Joona greatly reduces move count pruning,
below is the old and new move count limits starting from
ONE_PLY with half-play increment:

Old: 4,5,5,5, 7, 7,11,11,11,19,19,19,35,35
New: 4,5,7,9,12,15,19,23,28,33,39,45,52,59

Surprisingly results are even a bit better at a quite
fast time control.

After 5260 games at 30"+0.1
Mod - Orig:  864 - 806 - 3590  ELO +3 (+- 3.8)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-26 10:27:15 +01:00
Marco Costalba a28f4a56d3 Fix handling of 50 move rule and remove a fixme
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-25 11:24:20 +01:00
Marco Costalba 7305b56957 Shrink OutpostBonus[] definition
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-25 11:12:55 +01:00
Marco Costalba 045beac156 Simplify scale factors implementation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-24 12:56:45 +01:00
Marco Costalba 0e3535ea23 Rename no_mob_area in mobilityArea
It is the correct name.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-24 12:56:38 +01:00
Marco Costalba c254861ee6 Small code style in qsearch
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-21 20:04:15 +01:00
Marco Costalba c1558dde12 Do not update killers in qsearch
It seems totally unuseful because killers are not
used to order the moves in qsearch. Although there
is some functionality change, probably just a small
side effect.

After 5656 games on rc
Mod vs Orig: 1007 - 980 - 3669  ELO +1 (+- 3.7)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-21 06:25:47 +01:00
Marco Costalba 9d1522b24f A king move can never have negative SEE
So there is no need to explicitly check for king moves
when detecting prunable evasions.

Perhaps teoretically a very bit slower (I didn't test),
but it is more clear now what evasions we consider prunable.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-19 13:56:50 +01:00
Marco Costalba 23fd379694 Simplify SEE
Greatly cleanup SEE code and now it is also a bit
faster on gcc, about +0.6%.

Thanks to Mike Whiteley new SEE code that gave me
fresh ideas on how to cleanup this old stuff.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-19 13:41:54 +01:00
Marco Costalba 9ab0e1bb13 Retire NullMoveMargin
A code semplification that could even be a slight increase,
anyhow is a reducing pruning patch, so it is good even
at equal strenght.

After 6342 games
Mod - Orig:  1040 - 974 - 4328  ELO +3 (+- 3.5)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-16 08:22:39 +01:00
Marco Costalba debc815352 We need just one eval margin in search
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-14 18:06:10 +01:00
Marco Costalba dd8a076128 Reintroduce rook contact checks
Were removed when merged from Glaurung 2.2, but without
any test.

Note that weight has been increased from original 2 to 4 and
has been also fixed a bug where in the original version were
considered also diagonal sqaures for the rook, that are
contact squares but not checks.

After 4449 games at 30"+0.1
Mod - Orig:  717 - 649 - 3083  ELO +5 (+- 4.1)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-14 13:48:26 +01:00
Marco Costalba 4350d9e8a6 Fix a warning under icc
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-14 07:56:02 +01:00
Marco Costalba 42ed488987 Retire badCaptures[] array in MovePicker
Use the tail of moves[] array to store bad captures.

No functional change but some move reorder. Verified with old perft.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-13 13:22:00 +01:00
Marco Costalba 4143d00011 Increase QueenContactCheckBonus
And also other check bonuses.

After 4272 games on russian cluster at 30"+0.1
Mod - Orig:  711 - 612 - 2949  ELO +8 (+- 4.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-13 00:10:48 +01:00
Marco Costalba 1a2768705a Do not update king tables when we skip king safety
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-12 14:28:07 +01:00
Joona Kiiski fa2478a81f Retire pawn storm evaluation
More then 100 lines of almost useless evaluations. Prefer
code semplification to a very small and dubious advantage.

After 7457 games on russian cluster:
Mod - Orig: 1285 - 1334 - 4838  ELO -2 (+- 3.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-11 09:46:00 +01:00
Marco Costalba 5b5b496a6d Array FutilityMarginsMatrix stores Values
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-09-04 14:10:48 +01:00
Marco Costalba 6096ca7a95 Remove get_* prefix from RootMoveList API
And small additional cleanup in RootMoveList.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-30 12:56:12 +01:00
Marco Costalba 427dc2a82c Use only cumulativeNodes in RootMoveList
And rename in nodes now that we have only one.

After the beta-cut off counters removing we can
get rid also of this one.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-30 12:56:04 +01:00
Marco Costalba b177e6dd91 Use evaluation margins also in main search
For now keep FutilityMarginsMatrix[] unchanged, in
future we are going to reduce to compensate for extra
margin.

At this moment it is enough we don't have regressions.

After 9694 games on russian cluster
Mod - Orig 1608 - 1578 - 6508  ELO +1 (+- 2.8)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-28 12:07:15 +01:00
Marco Costalba d9dc9dbd65 Split branches in generate_piece_moves()
Instead of one comparison in while() condition use two,
the first to check if the piece is exsistant and the second
to loop across pieces of that type.

This should help branch prediction in cases we have only
one piece of the same type, for instance for queens, the
first branch is always true and the second is almost always
false.

Increased speed of 0.3-0.5 % on Gcc pgo compiles.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-27 13:28:07 +01:00
Marco Costalba 2a2353aac6 Speed up updateShelter()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 18:52:53 +02:00
Marco Costalba 1b4084b4a5 Assorted code style in evaluation.cpp
Renaming, cleanu up, etc.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:47:38 +01:00
Marco Costalba c7dd9b8d0c Finally remove value from EvalInfo
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:47:29 +01:00
Marco Costalba 9ee42d83b6 Remove dependency from ei.value in evaluate functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:28 +01:00
Marco Costalba 3107e68c03 Remove margin[] from EvalInfo
Directly pass arguments to king evaluation.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:28 +01:00
Marco Costalba d4250c52f0 Remove MaterialInfo* from EvalInfo
Use a local variable instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:27 +01:00
Marco Costalba 15d265cc66 Change evaluate() signature
Hide EvalInfo and return just the score and the margin.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:26 +01:00
Marco Costalba fff59319b0 Retire attackedBy[] access functions
Currently are used by evaluation itself and the
whole EvalInfo will be removed from global visibility
by next patch, so no reason to use them.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:26 +01:00
Marco Costalba b196af4dcd Decrypt some magics in bitboards definitions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-26 13:41:25 +01:00
Marco Costalba eb4858256b We don't need EvalInfo c'tor anymore
We know always get complete info from TT, so this
code is obsolete.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-25 00:20:53 +01:00
Marco Costalba c7a932bc74 Rename ei.kingDanger in ei.margin
It will be more clear when we will go to add stuff
apart from king danger itself.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 19:16:03 +01:00
Marco Costalba 00469d1798 Call apply_weight() only once in passed pawns evaluation
First accumulate the bonus for each pawn, then call the
not very fast apply_weight().

Should be no functional change apart from rounding issues.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 19:13:13 +01:00
Marco Costalba 17d820e248 Don't need to memset() EvalInfo
Set manually to zero the few fields that are
optionally populated and that's enough.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 18:58:51 +01:00
Marco Costalba d73eea3f71 There is no need of storing mobility in EvalInfo
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 18:55:40 +01:00
Marco Costalba 74033b004e Refresh comments in evaluate.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 18:55:31 +01:00
Marco Costalba 6125966da0 Unify single MobilityBonus[] tables in a big single one
Avoid one address lookup in a very critical time path.

Unified also outpost bonus tables for knights and bishops.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 13:34:41 +01:00
Marco Costalba 421867ea2d Retire trapped bishop evaluation
Another 100 lines of dubious and ad-hoc code.

After 7644 games on russian cluster:
Mod - Orig 1285 - 1249 - 5110  ELO +1 (+- 3.2)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-24 13:13:20 +01:00
Marco Costalba e17fa64aec Retire UCI_Chess960 option
We don't need that !

We can infere from starting fen string if we are in
a Chess960 game or not. And note that this is a per-position
property, not an application wide one.

A nice trick is to use a custom manipulator (that is an
enum actually) to keep using the handy operator<<() on the
move when sending to std::cout, yes, I have indulged a
bit here ;-)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-22 16:51:20 +01:00
Marco Costalba 7b721b3663 Prefetch pawn hash key
Plus a bunch of other minor optimizations.

With this power pack we have an increase
of a whopping 1.4%  :-)

...and it took 3 good hours of profiling + hacking to get it out !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-22 14:04:06 +01:00
Marco Costalba b6ba5f7fe4 Retire unstoppable pawns evaluation
One hundred lines of code should be compensated by an
important ELO increase, otherwise are candidate for removal...

...and is not the case. We are well within error margin, so
remove the code even if we lose a couple of elo points, but
semplification is huge.

After 6494 games on russian cluster
Orig vs Mod 1145 - 1107 - 4242 (-2 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-22 01:39:31 +01:00
Marco Costalba 73f1179d39 Remove my address from README
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-21 17:21:44 +01:00
Marco Costalba 0363b54358 Retire beta counters stuff
Is now obsoleted by previous patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-20 18:33:19 +01:00
Joona Kiiski a44f79141e Use MovePicker's move ordering also at root
After testing on our russian cluster: +3 elo after 4200 games

So keep it becuase it allows a good semplification.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-20 18:04:02 +01:00
Marco Costalba 79a28841f9 Move StartPositionFEN out of the header
It is not needed to have global visibility.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 18:22:48 +01:00
Marco Costalba df4b106716 Move piece values in piece.h / piece.cpp
Where they belong.

Note that array PieceValueMidgame[] and PieceValueEndgame[]
are now declared extern in the header and moved in piece.cpp
so to avoid allocate the array each time the header is
included !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 18:22:48 +01:00
Marco Costalba a952c6bc6d Retire is_upper_bound() and friend
Directly expand in the few places where is called.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 18:22:47 +01:00
Marco Costalba 391e176274 Retire useless piece_value_midgame() overloads
Directly access the table in the few call places.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 18:22:46 +01:00
Marco Costalba 5bed82cd4e Introduce and use SCORE_ZERO
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 18:22:46 +01:00
Marco Costalba 4419924fcf Do not score PH_QCHECKS
They are picked unsorted anyway, so score is unuseful.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 14:50:02 +01:00
Marco Costalba a5ae7fe260 Disable templetized operators by default
To avoid nasty bugs due to silently overriding of
common operator we enable the templates on a type
by type base using partial template specialization.

No functional change, zero overhead at runtime.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:41 +01:00
Marco Costalba 94b9c65e09 Introduce enum VALUE_ZERO instead of Value(0)
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:38 +01:00
Marco Costalba 0e800c527a Use Use templetized operations for Score and Value
Note that in value we leave two specialized functions
to allow adding an integer, we don't want to add this
as a template becasue we want to control implicit
conversions to integer of an enum.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:37 +01:00
Marco Costalba 13bd0cff0d Use templetized operations for Piece
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:35 +01:00
Marco Costalba 8e31764c49 Use templetized operations for Square
This is tricky because there are some special
binary fnctions with SquareDelta that we should
leave as they are.

Also note that we needed to add Unary minus template
to fix a comile error in SERIALIZE_MOVES_D macro that was
triggered because now we don't allow conversion to int.

No fuctional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:33 +01:00
Marco Costalba 4ce08482c3 Use templetized operations for File and Rank
Doing the conversion the compiler is now able to
spot two possible ambiguity calls that now we can
easily fix.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:30 +01:00
Marco Costalba 80bee85d5f Use templetize enum operations for Depth
Instead of hardcoded ones.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:28 +01:00
Marco Costalba 4f96f420a3 Store in TT with depth == -OnePly instead of -1
When depth < DEPTH_ZERO we store with the same depth all
the positions, use -OnePly instead of -1 for consistency
with depth arithmetic.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:26 +01:00
Marco Costalba 4f28e19fc0 (Re)introduce DEPTH_ZERO to replace Depth(0)
No functional changes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:24 +01:00
Marco Costalba ea2b8a93eb Retire some unused Depth operator() functions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:23 +01:00
Marco Costalba 4aeffc8c9a Rename OnePly in ONE_PLY
Use enum values standard naming policy also for this one.

No fuctional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:20 +01:00
Marco Costalba cfb52fcd5d Define OnePly as a Depth enum costant
There is no reason to use a variable for this.

Also remove unused DEPTH_ZERO and DEPTH_MAX.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-19 13:48:11 +01:00
Marco Costalba cf4c28ff86 Revert F_90 and F_92
Regression test found the patches to be harmless,
so revert to keep code simpler.

Test1 at 20+0.1: (2500 - 3000) +0 ELO
Test2 at 1+0: (~1000) +2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-18 10:07:28 +01:00
Marco Costalba 252537fd9c Cleanup and optimize Position::has_mate_threat()
There is a functional change because we now skip
more moves and because do_move() / undo_move() is
well known to be not reversible we end up with a
change in node count, although there is actually
no change but a bit speed up.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-09 12:30:33 +01:00
Marco Costalba f26e0fec64 Usual material.cpp small touches
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-08 13:14:18 +01:00
Marco Costalba e6376d9b8d Rename constants to use *_NONE scheme
To be uniform across the sources. As a nice side effect
I quickly spotted a couple of needed renames:

captured_piece() -> captured_piece_type()
st->capture      -> st->capturedType

Proposed by Ralph and done with QtCreator

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-07 18:56:24 +01:00
Marco Costalba 2170fa18bf Move depth computation out of fail low loop
In root_search() we can compute depth at the beginning
once and for all.

Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-06 22:30:50 +01:00
Marco Costalba be540b6dd7 Another push to perft speed
We don't need to generate captures and non
captures in a separate step. This gives another
7% push to perft speed.

yes, I know, it is totally useless :-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-06 12:10:07 +01:00
Marco Costalba 3a2cd37080 Faster perft
Skip moves scoring and sorting: this more then
doubles the speed !

Verified is correct.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-06 11:15:41 +01:00
Marco Costalba d6904157aa Rename TM in ThreadsMgr
This avoid misunderstandings with new TimeManager
object called TimeMgr.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-03 13:06:56 +01:00
Marco Costalba 5fc98745c3 TimeManager API rename
We can now set member data as private because is no more
directly accessed.

Should be more clear now.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-03 11:49:28 +01:00
Marco Costalba c295599e4a Move time related global variables under TimeManager
Move OptimumSearchTime, MaximumSearchTime and
ExtraSearchTime in TimeManager.

Note that we remove an useless initialization to 0 because
these variables are used only with time management.

Also introduce and use TimeManager::available_time()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-03 11:47:52 +01:00
Marco Costalba dda53e831d Introduce TimeManager class
Firt step in unifying all time management under
a single umbrella. Just introduced the class without
even member data.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-03 11:39:42 +01:00
Marco Costalba 977f6349a9 Small cleanup in search Step.5
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 22:09:20 +01:00
Marco Costalba 5aef9186ac Reset bestMove before entering main moves loop
After razoring, IID, null verification and singular
extension searches we have could have a dirty ss->bestMove,
restore to MOVE_NONE before to enter moves loop.

This should avoid to store in TT a stale move when
we fail low.

Tested together with previous patch that is the
one that gives ELO.

After 1152 games at 1+0 on my QUAD
Mod vs Orig +233 =716 -203 (+9 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 22:02:59 +01:00
Marco Costalba cbcc581a86 Use past SE information also for success cases
If singular extension search was succesful in the past then
skip another the SE search and extend of one ply.

Another way to mitigate the cost of SE at the price of
some more spurious extension, but on 90% of cases info
is correct.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 18:47:27 +01:00
Marco Costalba fe23c70cf1 Rename MaxSearchTime and AbsoluteMaxSearchTime
Renamed in OptimumSearchTime and MaximumSearchTime,
should be more clear now.

Suggested by Joona.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 18:41:55 +01:00
Marco Costalba cf0295f1ad Templetize xxx_time_for_MTG()
Also fixed some warnings under MSVC.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 11:55:45 +01:00
Marco Costalba 391cd57b52 Little timeman.cpp massage
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 11:49:49 +01:00
Joona Kiiski f40e481fd6 Tweak default values for ucioptions
I created three different systems, tested them all separately and
attached one did best:

1/40: Orig - Mod: 841 - 850 (+2 elo)
1+1 : Orig - Mod: 474 - 498 (+9 elo)
1+0 : Orig - Mod: 455 - 495 (+15 elo)

Because such testing system is not statistically reliable, I made a
confirmation test:

1/40: Orig - Mod: 502 - 543 (+14 elo)
1+1: Orig - Mod: 447 - 489 (+16 elo)
1+0: Orig - Mod: 641 - 656 (+4 elo)

All tests show positive score :-)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 11:49:31 +01:00
Joona Kiiski c0616d773d New Time management system
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-08-02 11:48:03 +01:00
Marco Costalba 87139d018c Always use ss->bestMove to store ply best move
Instead of ss->currentMove. It is more consistent and
clear to understand.

Remark by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-31 07:29:29 +01:00
Marco Costalba 9645e8e4e7 Lower SingularExtensionDepth to 7 plies for non-pv
To compensate for the extra work skip singular
searches deemed to fail because excluded node
failed high already in the past.

After 1200 games at 1+0
Mod vs Orig +387 =1274 -339  (+8 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-30 22:50:03 +01:00
Marco Costalba 935fc09fd4 Two small fixes in passed pawns evaluation
The one in evaluate_passed_pawns() is just a micro
optimization, the other in evaluate_unstoppable_pawns()
is indeed a fix, although almost unmeasurable in real
games.

Bugs report and fixes by Marek Kwiatkowski

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-26 06:10:01 +01:00
Marco Costalba 5ee7dfebf7 Fix KBNK endgame
Broken by recent patch. Also better document what's
happening there.

Verified to restore original behaviour.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 21:58:09 +01:00
Marco Costalba 6b6f3c4ca4 Rename EMPTY in NO_PIECE
It is more correct and more in line with enum PieceType

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 12:10:22 +01:00
Marco Costalba 14f059072a Introduce enum SquareColor
Square and piece colors are two different things,
so use different types to avoid misunderstandings.

Suggested by Tord.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 11:49:58 +01:00
Marco Costalba 9b1d5bd534 Introduce and use same_color_squares()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 11:49:32 +01:00
Marco Costalba a84e4b2049 Cleanup Position::print()
And remove not used OUTSIDE enum Piece.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 09:57:57 +01:00
Joona Kiiski 4d438fae9e Fix build failure on GCC
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-25 08:57:23 +01:00
Marco Costalba 02882dfe81 Cleanup Position::to_fen()
Less invasive then previous patches, but still a good
enhancement.

Also some indulge on STL algorithms :-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-24 18:56:07 +01:00
Marco Costalba c2048136ec Last touches to from_fen()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-24 16:31:12 +01:00
Marco Costalba 839088205e Rewrite Position::from_fen()
Complete rewrite the function and extend compatibility
also to X-FEN notation for Chess960.

We are now able to read standard FEN, Shredder-FEN and X-FEN.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-24 09:43:01 +01:00
Joona Kiiski 098ac5e44e Don't initialize psqt-tables when 'ucinewgame' is received
After 'Randomness' is retired, this is no longer necessary.

NOTE: Possibly some extra care is needed when tuning branch is synced

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 12:21:54 +01:00
Joona Kiiski d3260ce70f Retire 'Randomness' ucioption
Using multiple threads and good opening book is
much better and more reliable source of randomness than
spoiling psqt-tables

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 12:21:46 +01:00
Joona Kiiski 71ba48c4ff Always init pthread locks to NULL
This is the only way to keep Windows and POSIX behaviour in sync,
so better hardcode it.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 07:03:39 +01:00
Joona Kiiski 65f8b6dbc0 Remove other locking options
Currently broken and we use pthreads in search.cpp
anyway, so I see no reason to keep these around

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 07:03:32 +01:00
Marco Costalba d5520977b9 Retire SearchStack init() and initKillers()
Let be explicit about what this functions do, and
we save some code lines too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 02:42:27 +01:00
Marco Costalba d004ec924d Fix errouneus reset of ss->threatMove
After we set ss->threatMove we could go under a IID step that
resets SearchStack ss and so also ss->threatMove.

When later we use that field in futility pruning we have this
set to MOVE_NONE !

The fix is to use a local variable and add threatMove to SplitPoint
to pass this move to slaves.

Spotted by Ralph Stoesser, fix suggested by Richard Vida.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-23 02:26:57 +01:00
Marco Costalba 5c3aeae566 Revert previous patch
Improvement is easily in error bar and there is
some added complexity making future changes more
difficult.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-22 18:30:18 +01:00
Marco Costalba 26a8b84417 Weight backward-ness of a pawn
Because not all backward pawns are the same ;-) if the
blocking enemy pawn is near then our pawn is more backward
than another whose enemy pawn is far away so that can advance
for some sqaures.

After 2925 games at 30"+0 on my QUAD
Mod vs Orig +602 =1745 -578 +3 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-22 18:15:16 +01:00
Joona Kiiski 6aef4429fd ValueType needs only 2 bits to be stored in TT
Also update some more TT documentation

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-22 17:51:33 +01:00
Joona Kiiski 23db43e698 Update TT documentation
Update outdated and even misleading documentation.

Also check #include-directives

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-22 17:51:26 +01:00
Marco Costalba 6f6f59ea6a Move insert_pv() and extract_pv() out of TT class
These functions have little to do with TranspositionTable
class and more with the search and in particular with the PV
handling. So move them where they belong.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-21 12:09:09 +01:00
Marco Costalba e2c0b5f995 Store position static score in TT as soon as possible
So to maximize the possibility to avoid to recalculate it
in the future. A small speed-up of 0.8%

Idea by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-21 12:08:45 +01:00
Marco Costalba 02f96fcf5e Introduce DEPTH_NONE and use it
Also better fix previous patch.

Suggestions by Joona and Ralph.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-20 19:06:09 +01:00
Joona Kiiski 28feb2c6b0 Remove pointless tte->static_value() != VALUE_NONE checks
Now in non-check nodes we are guaranteed to always have static value
in TT Entry.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-20 19:01:10 +01:00
Joona Kiiski ba1a44f216 Store static value and king danger in TT also in TT.insert_pv() method
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-20 19:01:01 +01:00
Marco Costalba 0fb5d7a737 Fix "pass ss->eval to qsearch()" condition
The seocond check is no more needed now and
anyhow is wrong to overwrite a TT entry if
present.

Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 12:28:33 +01:00
Marco Costalba 201f924d53 Triviality in material.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 12:01:36 +01:00
Marco Costalba 95388a952b Small rewrite of backward pawn test
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 07:13:50 +01:00
Joona Kiiski b5178597bd Initialize SearchStack only once at RootMoveList c'tor
Just fix current ugly behaviour :-)

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 03:53:37 +01:00
Joona Kiiski b75e68860c Every node is responsible for initializing its own SearchStack entry
More logical than doing partly initialization at init_ss_array()

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 03:52:21 +01:00
Joona Kiiski 66c5835080 Drop KILLER_MAX. Hardcode to 2 instead.
KILLER_MAX in search.h is quite pointless, because
we already hardcode this to 2 in MovePicker anyway.

By hard-coding this to 2 we can keep code simpler.

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 03:51:25 +01:00
Joona Kiiski a6d13428f6 Do not initialize ss->reduction to zero in the beginning of node
It must already be zero because zeroed in SearchStack initialization

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 03:48:51 +01:00
Joona Kiiski 1322ab97c7 Do not reset ss->eval in the beginning of the node
This avoids problems with IID clearing ss->eval and
eval not being available when we return

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-19 03:43:58 +01:00
Marco Costalba 6e06db93fd Fix isolated and backward pawns scoring
It is more clear and also more correct because we
consider enemy pawns only in fornt of us and not just
on our file.

Very small functional change, almost not measurable, but
keep the patch for documenting purposes.

Spotted by Marek Kwiatkowski.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-17 17:32:54 +01:00
Marco Costalba 53bbcb78d5 Triviality in endgame.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-17 14:00:25 +01:00
Joona Kiiski a6dcaa575f Update Makefile
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-16 12:04:02 +01:00
Joona Kiiski 05c5442633 Find balance between 1.7 and 1.8 reductions
Almost no change so commit because is a pruning
reduction patch.

After 1088 games at 1'+0 with QUAD
Mod vs Orig +178 =727 -183  (-2 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-15 20:53:16 +01:00
Marco Costalba b6ab610e2f Remove redundant argument in think()
We don't need to pass side_to_move because we can get
it directly from the position object.

Note that in benchmark we always used to pass '0' and
it was a bug, but with no effect because was used only
in time[] and increment[], set always to 0 for both
colors.

Also additional small cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-15 17:14:30 +01:00
Marco Costalba a98dee7835 Retire apply_scale_factor() and scale.h
Directly inline in the only occurence.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-15 17:05:23 +01:00
Marco Costalba 3e38e61565 Inline history and gain getters
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-15 16:55:35 +01:00
Marco Costalba bc0c1c8d7b Retire value.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-15 12:16:36 +01:00
Marco Costalba 605b3aedd5 Retire LSN machinery
Now that we use cutechess-cli we can set the auto-resign
parameter that makes LSN less effective.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-14 17:29:36 +01:00
Marco Costalba 8547798345 Triviality in ucioption.cpp
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-14 17:29:34 +01:00
Marco Costalba 3703d12eb9 Add moves from failed high nodes in PV
Considering only VALUE_TYPE_EXACT nodes is too
restrictive and has a number of side-effects, most
notably the truncation of PV line after a fail high
at root.

Note that in this way we are no more guaranteed that
PV line is built up with PV nodes only, because
it could happen that a side search overwrites with a
cut-off move a PV node and this cut-off move ends up
in PV.

Change should be almost not measurable, perhaps with
ponder on we could have some beneficial effect.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-12 21:06:40 +01:00
Marco Costalba a47a7dadeb Fix (zugzwang) verification to be shallower then null search
Currently starting from depth 12*OnePly on we have a verification
search deeper then the null search.

Note that, although reduction is R we start from one ply less then
null search, so actually we reach a depth that is OnePly shallower
then null search.

After 1130 games at 1'+0 on QUAD
Mod vs Orig +202 =756 -172  +9 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-12 21:03:30 +01:00
Joona Kiiski 00e86078a5 Remove TranspositionTable::overwrites variable
Doesn't provide useful information and
can cause slowdown with many Threads.

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-11 22:25:02 +01:00
Marco Costalba 2adbb80b8b Space inflate bitbase.cpp
Also heavy cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-11 12:05:06 +01:00
Marco Costalba ee8cdb1721 There is no need to clear TT at allocation time
Operator new() already returns a zeroed memory.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-11 09:42:04 +01:00
Marco Costalba 82bd61a8fa Revert previous patch
After the previous patch, it's impossible to build
anything else than x86 32-bit binary!

So revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-10 16:43:05 +01:00
Marco Costalba 87502c0fcb Makefile: default on gcc 32 bits when type 'make'
From Vratko Polak

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-10 12:16:43 +01:00
Marco Costalba aa172032c4 Reword singular extension comments
Should be more stick to original definition (Hsu, Campbell)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-06 17:56:04 +01:00
Joona Kiiski 8689ff7d03 Tweak Makefile a bit
To fix some build problems on debian's
automatic building system.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-06 06:24:58 +01:00
Marco Costalba 04e1ba8aa2 Move SplitPoint array under its thread
And cleanup / rename that part of code.

No functional change also with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-05 20:55:01 +01:00
Marco Costalba 2dfec0f614 Tweak non-captures scoring
Tested with Orig set at f5ef5632f so to evaluate
direct gain against 1.8

After 3239 games at 10"+0.1
Mod vs Orig +701 =1906 -632 +7 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-04 21:49:23 +01:00
Joona Kiiski e0056c3851 Fix TT documentation
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-04 20:15:09 +01:00
Marco Costalba a5c85d3cfc Reintroduce GCC/ICC rounding hack
Unfortunatly the source of this issue is not in the
different handling of log(0) illegal value.

No functional change on MSVC.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 19:05:04 +01:00
Joona Kiiski d0fdc20231 Fix Makefile for HPUX
On hpux there is no prefetch.

Reported by Richard Lloyd

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 18:54:34 +01:00
Marco Costalba 1d4e7bbdf5 Fix DIVIDE BY ZERO exception in init_search()
It happens that when d == 0 we calculate:

log(double(0 * 0) / 2)

Unfortunately, log(0) is "illegal" and can generate either a
floating point exception or return a nonsense "huge" value
depending on the platform.

This fixs in the proper way the GCC/ICC rounding difference,
bug was from our side, not in the intel compiler.

Also fixed some few other warnings.

Bug spotted by Richard Lloyd.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 16:12:20 +01:00
Marco Costalba 3578207974 PSQT access functions can be static
Also renamed history access value in something more
in line with the meaning.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 05:49:13 +01:00
Marco Costalba 40ad5194aa Use only history to score non-captures
It seems there is absolutely no difference in using gains.

After 7025 games at 5"+0
Mod vs Orig +1903 =3236 -1886 (+1 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 05:36:53 +01:00
Marco Costalba f5ef5632ff Restore development version
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-03 05:34:57 +01:00
Marco Costalba 21de03fad7 Revert "Another PSQT tuning round"
At longer TC of 1'+0" patch fails:
Orig - Mod: 841 - 819 (-6 elo!)

Just before the release ;-)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-02 06:55:03 +01:00
Marco Costalba 2d635f7b74 Stockfish 1.8
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-02 06:23:15 +01:00
Marco Costalba b50dc1647f Mark CheckInfo c'tor as explicit
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-01 22:21:09 +01:00
Marco Costalba 971c591be7 Move singleEvasion assignment out of move's loop
We don't need to recheck after every move.

Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-07-01 22:18:53 +01:00
Joona Kiiski b24a2dfc72 Another PSQT tuning round
This time with a new algorithm by Joona.

It works basically like this:

repeat
{
   1) pick 8000 random positions from qsearch
   2) "go depth 8" to get the true evaluation.
   3) "eval" to get the stand pat score
   4) Adjusting parameters one by one to minimize deltasum between
true evaluation and stand  pat scores.
}

* Good news: method seems to converge
* Bad news: Point where it converges is not optimum.

So it's more or less trial and error... sometimes works, sometimes
doesn't. It can give you the right direction, but if you let it run
too long, it fails. Far from scientific ;)

After 14800 games with 5s/game
Orig - Mod: 3318 - 3570 - 7626 (+6 elo)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-29 18:52:51 +01:00
Marco Costalba 4d170725ab Remove a redundant check in passed pawn eval
When first condition is met then second one is
always true.

Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-29 18:35:54 +01:00
Marco Costalba aad8c82cf6 Code style triviality in san.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-29 18:33:43 +01:00
Marco Costalba 6c0a37bbf2 Rename TranspositionTable 'writes' in 'overwrites'
Better documents what that variable means.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-29 18:23:45 +01:00
Marco Costalba 5c3ebd1fbf Extract only exact scores to get the PV
This should allow to skip overwritten nodes because
only in PV we store in TT with VALUE_TYPE_EXACT flag.

Test result for the whole series is:

After 3627 games at 5"
Mod vs Orig +1037 =1605 -985 +5 ELO

After 1311 games at 1'+0"
Mod vs Orig +234 =850 -227 +2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-29 18:23:28 +01:00
Marco Costalba 62c68c2d21 Retire update_pv() and sp_update_pv()
Expand inline instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 15:43:54 +01:00
Marco Costalba adb43cc0cc Retire pv[] from SearchStack
Extract PV info from TT instead of using
a set of arrays. This is almost equivalent
except for cases when TT is full and the PV entry
is overwritten, but this is very rare.

(Almost) No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 15:13:39 +01:00
Marco Costalba 0a687b2cf0 Introduce bestMove to store PV move
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 14:42:44 +01:00
Marco Costalba eb48c54687 Cleanup code that stores score in TT
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 11:01:43 +01:00
Joona Kiiski 3c3b129e7b Fix some wrong documentation
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 10:23:31 +01:00
Joona Kiiski 918533dc06 Remove unused constant
Fixes warning on ICC

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-26 10:22:26 +01:00
Marco Costalba 0ac44b40c9 Stockfish 1.8 beta 2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-25 18:56:42 +01:00
Joona Kiiski b839ea6c0c Hack to fix GCC/ICC rounding difference
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-25 18:54:33 +01:00
62 changed files with 3960 additions and 5180 deletions
+1 -6
View File
@@ -3,7 +3,7 @@
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI not a complete chess program, but requires some UCI compatible GUI
(like XBoard with PolyGlot, eboard, Jos, Arena, Sigma Chess, Shredder, (like XBoard with PolyGlot, eboard, Josè, Arena, Sigma Chess, Shredder,
Chess Partner, or Fritz) in order to be used comfortably. Read the Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use documentation for your GUI of choice for information about how to use
Stockfish with your GUI. Stockfish with your GUI.
@@ -83,8 +83,3 @@ source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named For full details, read the copy of the GPL found in the file named
Copying.txt. Copying.txt.
6. Feedback
-----------
The author's e-mail address is mcostalba@gmail.com
+31 -10
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 value.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
### ========================================================================== ### ==========================================================================
@@ -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,10 +227,18 @@ ifeq ($(COMP),icc)
endif endif
### 3.2 General compiler settings ### 3.2 General compiler settings
CXXFLAGS += -g -Wall -fno-exceptions -fno-rtti $(EXTRACXXFLAGS) CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra
endif
ifeq ($(comp),mingw)
CXXFLAGS += -Wno-long-long -Wextra
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)
@@ -230,7 +246,7 @@ ifeq ($(os),osx)
endif endif
### 3.3 General linker settings ### 3.3 General linker settings
LDFLAGS += -lpthread $(EXTRALDFLAGS) LDFLAGS = -lpthread $(EXTRALDFLAGS)
ifeq ($(os),osx) ifeq ($(os),osx)
LDFLAGS += -arch $(arch) LDFLAGS += -arch $(arch)
@@ -257,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
@@ -336,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 ""
@@ -408,7 +429,7 @@ install:
-strip $(BINDIR)/$(EXE) -strip $(BINDIR)/$(EXE)
clean: clean:
$(RM) $(EXE) *.o .depend *~ core bench.txt $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
testrun: testrun:
@$(PGOBENCH) @$(PGOBENCH)
@@ -449,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)
@@ -500,7 +521,7 @@ icc-profile-clean:
hpux: hpux:
$(MAKE) \ $(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG' \ CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \ CXXFLAGS="" \
LDFLAGS="" \ LDFLAGS="" \
all all
-78
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);
}
-39
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)
+39 -71
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 - -",
@@ -51,8 +49,9 @@ const string BenchmarkPositions[] = {
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"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,19 +123,19 @@ 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 * OnePly); 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;
totalNodes += perftCnt; totalNodes += perftCnt;
} else { } else {
if (!think(pos, false, false, 0, 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
} }
-37
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)
+133 -164
View File
@@ -24,9 +24,7 @@
#include <cassert> #include <cassert>
#include "bitbase.h"
#include "bitboard.h" #include "bitboard.h"
#include "move.h"
#include "square.h" #include "square.h"
@@ -46,30 +44,22 @@ namespace {
struct KPKPosition { struct KPKPosition {
void from_index(int index); void from_index(int index);
int to_index() const;
bool is_legal() const; bool is_legal() const;
bool is_immediate_draw() const; bool is_immediate_draw() const;
bool is_immediate_win() const; bool is_immediate_win() const;
Bitboard wk_attacks() const; Bitboard wk_attacks() const { return StepAttackBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const; Bitboard bk_attacks() const { return StepAttackBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const; Bitboard pawn_attacks() const { return StepAttackBB[WP][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare; Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove; Color sideToMove;
}; };
const int IndexMax = 2 * 24 * 64 * 64;
Result *Bitbase; Result classify_wtm(const KPKPosition& pos, const Result bb[]);
const int IndexMax = 2*24*64*64; Result classify_btm(const KPKPosition& pos, const Result bb[]);
int UnknownCount = 0;
void initialize();
bool next_iteration();
Result classify_wtm(const KPKPosition &p);
Result classify_btm(const KPKPosition &p);
int compute_index(Square wksq, Square bksq, Square psq, Color stm); int compute_index(Square wksq, Square bksq, Square psq, Color stm);
int compress_result(Result r);
} }
@@ -78,175 +68,157 @@ namespace {
//// ////
void generate_kpk_bitbase(uint8_t bitbase[]) { void generate_kpk_bitbase(uint8_t bitbase[]) {
// Allocate array and initialize:
Bitbase = new Result[IndexMax];
initialize();
// Iterate until all positions are classified: bool repeat;
while(next_iteration());
// Compress bitbase into the supplied parameter:
int i, j, b; int i, j, b;
for(i = 0; i < 24576; i++) { KPKPosition pos;
for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++); Result bb[IndexMax];
assert(b == int(uint8_t(b)));
bitbase[i] = (uint8_t)b; // Initialize table
for (i = 0; i < IndexMax; i++)
{
pos.from_index(i);
bb[i] = !pos.is_legal() ? RESULT_INVALID
: pos.is_immediate_draw() ? RESULT_DRAW
: pos.is_immediate_win() ? RESULT_WIN : RESULT_UNKNOWN;
} }
// Release allocated memory: // Iterate until all positions are classified (30 cycles needed)
delete [] Bitbase; do {
repeat = false;
for (i = 0; i < IndexMax; i++)
if (bb[i] == RESULT_UNKNOWN)
{
pos.from_index(i);
bb[i] = (pos.sideToMove == WHITE) ? classify_wtm(pos, bb)
: classify_btm(pos, bb);
if (bb[i] != RESULT_UNKNOWN)
repeat = true;
}
} while (repeat);
// Compress result and map into supplied bitbase parameter
for (i = 0; i < 24576; i++)
{
b = 0;
for (j = 0; j < 8; j++)
if (bb[8*i+j] == RESULT_WIN || bb[8*i+j] == RESULT_LOSS)
b |= (1 << j);
bitbase[i] = (uint8_t)b;
}
} }
namespace { namespace {
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
assert(r >= 0 && r < IndexMax);
return r;
}
void KPKPosition::from_index(int index) { void KPKPosition::from_index(int index) {
int s;
int s = (index / 8192) % 24;
sideToMove = Color(index % 2); sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64); blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64); whiteKingSquare = Square((index / 128) % 64);
s = (index / 8192) % 24;
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1)); pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
} }
int KPKPosition::to_index() const {
return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
sideToMove);
}
bool KPKPosition::is_legal() const { bool KPKPosition::is_legal() const {
if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
pawnSquare == blackKingSquare) if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| pawnSquare == blackKingSquare)
return false; return false;
if(sideToMove == WHITE) {
if(bit_is_set(this->wk_attacks(), blackKingSquare)) if (sideToMove == WHITE)
return false; {
if(bit_is_set(this->pawn_attacks(), blackKingSquare)) if ( bit_is_set(wk_attacks(), blackKingSquare)
|| bit_is_set(pawn_attacks(), blackKingSquare))
return false; return false;
} }
else { else if (bit_is_set(bk_attacks(), whiteKingSquare))
if(bit_is_set(this->bk_attacks(), whiteKingSquare))
return false; return false;
}
return true; return true;
} }
bool KPKPosition::is_immediate_draw() const { bool KPKPosition::is_immediate_draw() const {
if(sideToMove == BLACK) {
Bitboard wka = this->wk_attacks(); if (sideToMove == BLACK)
Bitboard bka = this->bk_attacks(); {
Bitboard wka = wk_attacks();
Bitboard bka = bk_attacks();
// Case 1: Stalemate // Case 1: Stalemate
if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB) if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB)
return true; return true;
// Case 2: King can capture pawn // Case 2: King can capture pawn
if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare)) if (bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true; return true;
} }
else { else
{
// Case 1: Stalemate // Case 1: Stalemate
if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 && if ( whiteKingSquare == SQ_A8
(blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8)) && pawnSquare == SQ_A7
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true; return true;
} }
return false; return false;
} }
bool KPKPosition::is_immediate_win() const { bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the white
// pawn can be promoted without getting captured: // The position is an immediate win if it is white to move and the
return // white pawn can be promoted without getting captured.
sideToMove == WHITE && return sideToMove == WHITE
square_rank(pawnSquare) == RANK_7 && && square_rank(pawnSquare) == RANK_7
(square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 || && ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N)); || bit_is_set(wk_attacks(), pawnSquare + DELTA_N));
} }
Result classify_wtm(const KPKPosition& pos, const Result bb[]) {
Bitboard KPKPosition::wk_attacks() const {
return StepAttackBB[WK][whiteKingSquare];
}
Bitboard KPKPosition::bk_attacks() const {
return StepAttackBB[BK][blackKingSquare];
}
Bitboard KPKPosition::pawn_attacks() const {
return StepAttackBB[WP][pawnSquare];
}
void initialize() {
KPKPosition p;
for(int i = 0; i < IndexMax; i++) {
p.from_index(i);
if(!p.is_legal())
Bitbase[i] = RESULT_INVALID;
else if(p.is_immediate_draw())
Bitbase[i] = RESULT_DRAW;
else if(p.is_immediate_win())
Bitbase[i] = RESULT_WIN;
else {
Bitbase[i] = RESULT_UNKNOWN;
UnknownCount++;
}
}
}
bool next_iteration() {
KPKPosition p;
int previousUnknownCount = UnknownCount;
for(int i = 0; i < IndexMax; i++)
if(Bitbase[i] == RESULT_UNKNOWN) {
p.from_index(i);
Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
Bitbase[i] == RESULT_DRAW)
UnknownCount--;
}
return UnknownCount != previousUnknownCount;
}
Result classify_wtm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_LOSS, the result // If one move leads to a position classified as RESULT_LOSS, the result
// of the current position is RESULT_WIN. If all moves lead to positions // of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified as // classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// RESULT_DRAW. Otherwise, the current position is classified as // otherwise the current position is classified as RESULT_UNKNOWN.
// RESULT_UNKNOWN.
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
int idx;
// King moves // King moves
b = p.wk_attacks(); b = pos.wk_attacks();
while(b) { while (b)
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare, idx = compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK);
BLACK)]) {
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID: case RESULT_DRAW:
case RESULT_INVALID:
break; break;
default: default:
@@ -255,37 +227,45 @@ namespace {
} }
// Pawn moves // Pawn moves
if(square_rank(p.pawnSquare) < RANK_7) { if (square_rank(pos.pawnSquare) < RANK_7)
s = p.pawnSquare + DELTA_N; {
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s, s = pos.pawnSquare + DELTA_N;
BLACK)]) { idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK);
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID: case RESULT_DRAW:
case RESULT_INVALID:
break; break;
default: default:
assert(false); assert(false);
} }
if(square_rank(s) == RANK_3 && // Double pawn push
s != p.whiteKingSquare && s != p.blackKingSquare) { if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{
s += DELTA_N; s += DELTA_N;
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s, idx = compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK);
BLACK)]) {
switch (bb[idx]) {
case RESULT_LOSS: case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID: case RESULT_DRAW:
case RESULT_INVALID:
break; break;
default: default:
@@ -293,12 +273,11 @@ namespace {
} }
} }
} }
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
} }
Result classify_btm(const KPKPosition &p) { Result classify_btm(const KPKPosition& pos, const Result bb[]) {
// If one move leads to a position classified as RESULT_DRAW, the result // If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions // of the current position is RESULT_DRAW. If all moves lead to positions
@@ -309,42 +288,32 @@ namespace {
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
int idx;
// King moves // King moves
b = p.bk_attacks(); b = pos.bk_attacks();
while(b) { while (b)
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare, idx = compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE);
WHITE)]) {
switch (bb[idx]) {
case RESULT_DRAW: case RESULT_DRAW:
return RESULT_DRAW; return RESULT_DRAW;
case RESULT_UNKNOWN: case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_WIN: case RESULT_INVALID: case RESULT_WIN:
case RESULT_INVALID:
break; break;
default: default:
assert(false); assert(false);
} }
} }
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS;
return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
}
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
assert(result >= 0 && result < IndexMax);
return result;
}
int compress_result(Result r) {
return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
} }
} }
-38
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)
+36 -33
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)
@@ -163,7 +162,10 @@ const int RShift[64] = {
#endif // defined(IS_64BIT) #endif // defined(IS_64BIT)
const Bitboard SquaresByColorBB[2] = { BlackSquaresBB, WhiteSquaresBB }; const Bitboard LightSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard DarkSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard SquaresByColorBB[2] = { DarkSquaresBB, LightSquaresBB };
const Bitboard FileBB[8] = { const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
@@ -223,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];
@@ -244,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);
@@ -286,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);
@@ -400,23 +400,8 @@ 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(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() {
@@ -469,29 +454,47 @@ 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);
} }
} }
}
Bitboard index_to_bitboard(int index, Bitboard mask) { Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = 0ULL; Bitboard result = 0ULL;
int bits = count_1s(mask); int bits = count_1s<CNT32>(mask);
for (int i = 0; i < bits; i++) for (int i = 0; i < bits; i++)
{ {
@@ -508,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]));
+25 -29
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"
@@ -36,28 +35,25 @@
//// Constants and variables //// Constants and variables
//// ////
const Bitboard EmptyBoardBB = 0ULL; const Bitboard EmptyBoardBB = 0;
const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL; const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = 0x0202020202020202ULL; const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = 0x0404040404040404ULL; const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = 0x0808080808080808ULL; const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = 0x1010101010101010ULL; const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = 0x2020202020202020ULL; const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = 0x4040404040404040ULL; const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = 0x8080808080808080ULL; const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFFULL; const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = 0xFF00ULL; const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = 0xFF0000ULL; const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = 0xFF000000ULL; const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = 0xFF00000000ULL; const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = 0xFF0000000000ULL; const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = 0xFF000000000000ULL; const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = 0xFF00000000000000ULL; const Bitboard Rank8BB = Rank1BB << (8 * 7);
extern const Bitboard SquaresByColorBB[2]; extern const Bitboard SquaresByColorBB[2];
extern const Bitboard FileBB[8]; extern const Bitboard FileBB[8];
@@ -71,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];
@@ -212,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
@@ -310,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.
+40 -49
View File
@@ -24,27 +24,23 @@
#include "types.h" #include "types.h"
// Select type of intrinsic bit count instruction to use, see enum BitCountType {
// README.txt on how to pgo compile with POPCNT support. CNT64,
#if !defined(USE_POPCNT) CNT64_MAX15,
#define POPCNT_INTRINSIC(x) 0 CNT32,
#elif defined(_MSC_VER) CNT32_MAX15,
#define POPCNT_INTRINSIC(x) (int)__popcnt64(x) CNT_POPCNT
#elif defined(__GNUC__) };
#define POPCNT_INTRINSIC(x) ({ \ /// count_1s() counts the number of nonzero bits in a bitboard.
unsigned long __ret; \ /// We have different optimized versions according if platform
__asm__("popcnt %1, %0" : "=r" (__ret) : "r" (x)); \ /// is 32 or 64 bits, and to the maximum number of nonzero bits.
__ret; }) /// We also support hardware popcnt instruction. See Readme.txt
/// on how to pgo compile with popcnt support.
template<BitCountType> inline int count_1s(Bitboard);
#endif template<>
inline int count_1s<CNT64>(Bitboard b) {
/// Software implementation of bit count functions
#if defined(IS_64BIT)
inline int count_1s(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -52,16 +48,16 @@ inline int count_1s(Bitboard b) {
return int(b >> 56); return int(b >> 56);
} }
inline int count_1s_max_15(Bitboard b) { template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL; b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; b *= 0x1111111111111111ULL;
return int(b >> 60); return int(b >> 60);
} }
#else // if !defined(IS_64BIT) template<>
inline int count_1s<CNT32>(Bitboard b) {
inline int count_1s(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -73,7 +69,8 @@ inline int count_1s(Bitboard b) {
return int(v >> 24); return int(v >> 24);
} }
inline int count_1s_max_15(Bitboard b) { template<>
inline int count_1s<CNT32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -84,27 +81,21 @@ inline int count_1s_max_15(Bitboard b) {
return int(v >> 28); return int(v >> 28);
} }
#endif // BITCOUNT template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
/// count_1s() counts the number of nonzero bits in a bitboard. return int(b != 0); // Avoid 'b not used' warning
/// If template parameter is true an intrinsic is called, otherwise #elif defined(_MSC_VER)
/// we fallback on a software implementation. return __popcnt64(b);
#elif defined(__GNUC__)
template<bool UseIntrinsic> unsigned long ret;
inline int count_1s(Bitboard b) { __asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s(b); #endif
}
template<bool UseIntrinsic>
inline int count_1s_max_15(Bitboard b) {
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s_max_15(b);
} }
// Detect hardware POPCNT support /// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() { inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1}; int CPUInfo[4] = {-1};
@@ -113,9 +104,9 @@ inline bool cpu_has_popcnt() {
} }
// Global constant initialized at startup that is set to true if /// CpuHasPOPCNT is a global constant initialized at startup that
// CPU on which application runs supports POPCNT intrinsic. Unless /// is set to true if CPU on which application runs supports popcnt
// USE_POPCNT is not defined. /// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT) #if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt(); const bool CpuHasPOPCNT = cpu_has_popcnt();
#else #else
@@ -123,12 +114,12 @@ const bool CpuHasPOPCNT = false;
#endif #endif
// Global constant used to print info about the use of 64 optimized /// CpuIs64Bit is a global constant initialized at compile time that
// functions to verify that a 64 bit compile has been correctly built. /// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT) #if defined(IS_64BIT)
const bool CpuHas64BitPath = true; const bool CpuIs64Bit = true;
#else #else
const bool CpuHas64BitPath = false; const bool CpuIs64Bit = false;
#endif #endif
#endif // !defined(BITCOUNT_H_INCLUDED) #endif // !defined(BITCOUNT_H_INCLUDED)
+56 -64
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,32 +364,24 @@ 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;
// Get the book size in number of entries // Get the book size in number of entries
seekg(0, ios::end); seekg(0, ios::end);
bookSize = tellg() / EntrySize; bookSize = long(tellg()) / EntrySize;
seekg(0, ios::beg); seekg(0, ios::beg);
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++)
{
Bitboard b = pos.pieces_of_color(c);
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); Square s = pop_1st_bit(&b);
Piece p = pos.piece_on(s); result ^= book_piece_key(pos.piece_on(s), 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;
} }
} }
+3 -9
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)
+8 -2
View File
@@ -21,6 +21,7 @@
#if !defined(COLOR_H_INCLUDED) #if !defined(COLOR_H_INCLUDED)
#define COLOR_H_INCLUDED #define COLOR_H_INCLUDED
#include "types.h"
//// ////
//// Types //// Types
@@ -32,13 +33,18 @@ enum Color {
COLOR_NONE COLOR_NONE
}; };
enum SquareColor {
DARK,
LIGHT
};
ENABLE_OPERATORS_ON(Color);
//// ////
//// Inline functions //// Inline functions
//// ////
inline void operator++ (Color &c, int) { c = Color(int(c) + 1); }
inline Color opposite_color(Color c) { inline Color opposite_color(Color c) {
return Color(int(c) ^ 1); return Color(int(c) ^ 1);
} }
+11 -29
View File
@@ -21,41 +21,23 @@
#if !defined(DEPTH_H_INCLUDED) #if !defined(DEPTH_H_INCLUDED)
#define DEPTH_H_INCLUDED #define DEPTH_H_INCLUDED
#include "types.h"
//// ////
//// Types //// Types
//// ////
enum Depth { enum Depth {
DEPTH_ZERO = 0,
DEPTH_MAX = 200, // 100 * OnePly; ONE_PLY = 2,
DEPTH_ENSURE_SIGNED = -1
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
}; };
ENABLE_OPERATORS_ON(Depth);
////
//// Constants
////
const Depth OnePly = Depth(2);
////
//// Inline functions
////
inline Depth operator+ (Depth d, int i) { return Depth(int(d) + i); }
inline Depth operator+ (Depth d1, Depth d2) { return Depth(int(d1) + int(d2)); }
inline void operator+= (Depth &d, int i) { d = Depth(int(d) + i); }
inline void operator+= (Depth &d1, Depth d2) { d1 += int(d2); }
inline Depth operator- (Depth d, int i) { return Depth(int(d) - i); }
inline Depth operator- (Depth d1, Depth d2) { return Depth(int(d1) - int(d2)); }
inline void operator-= (Depth &d, int i) { d = Depth(int(d) - i); }
inline void operator-= (Depth &d1, Depth d2) { d1 -= int(d2); }
inline Depth operator* (Depth d, int i) { return Depth(int(d) * i); }
inline Depth operator* (int i, Depth d) { return Depth(int(d) * i); }
inline void operator*= (Depth &d, int i) { d = Depth(int(d) * i); }
inline Depth operator/ (Depth d, int i) { return Depth(int(d) / i); }
inline void operator/= (Depth &d, int i) { d = Depth(int(d) / i); }
#endif // !defined(DEPTH_H_INCLUDED) #endif // !defined(DEPTH_H_INCLUDED)
-87
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;
}
}
}
}
-92
View File
@@ -1,92 +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
};
////
//// Variables
////
extern uint8_t DirectionTable[64][64];
extern uint8_t SignedDirectionTable[64][64];
////
//// Inline functions
////
inline void operator++ (Direction& d, int) {
d = Direction(int(d) + 1);
}
inline void operator++ (SignedDirection& d, int) {
d = SignedDirection(int(d) + 1);
}
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)
+98 -100
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"
@@ -65,13 +64,13 @@ namespace {
// the two kings in basic endgames. // the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Bitbase for KP vs K
uint8_t KPKBitbase[24576];
// Penalty for big distance between king and knight for the defending king // Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames. // and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 }; const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Bitbase for KP vs K
uint8_t KPKBitbase[24576];
// Various inline functions for accessing the above arrays // Various inline functions for accessing the above arrays
inline Value mate_table(Square s) { inline Value mate_table(Square s) {
return Value(MateTable[s]); return Value(MateTable[s]);
@@ -99,6 +98,16 @@ namespace {
//// Functions //// Functions
//// ////
/// init_bitbases() is called during program initialization, and simply loads
/// 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.
extern void generate_kpk_bitbase(uint8_t bitbase[]);
void init_bitbases() {
generate_kpk_bitbase(KPKBitbase);
}
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the /// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
@@ -106,8 +115,8 @@ namespace {
template<> template<>
Value EvaluationFunction<KXK>::apply(const Position& pos) const { Value EvaluationFunction<KXK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == Value(0)); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
@@ -117,13 +126,13 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) const {
+ mate_table(loserKSq) + mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq)); + distance_bonus(square_distance(winnerKSq, loserKSq));
if ( pos.piece_count(strongerSide, QUEEN) > 0 if ( pos.piece_count(strongerSide, QUEEN)
|| pos.piece_count(strongerSide, ROOK) > 0 || pos.piece_count(strongerSide, ROOK)
|| pos.piece_count(strongerSide, BISHOP) > 1) || pos.piece_count(strongerSide, BISHOP) > 1)
// TODO: check for two equal-colored bishops! // TODO: check for two equal-colored bishops!
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
return (strongerSide == pos.side_to_move() ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -132,8 +141,8 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) const {
template<> template<>
Value EvaluationFunction<KBNK>::apply(const Position& pos) const { Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == Value(0)); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1);
@@ -143,7 +152,10 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0); Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0);
if (square_color(bishopSquare) == BLACK) // kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1.
if (!same_color_squares(bishopSquare, SQ_A1))
{ {
winnerKSq = flop_square(winnerKSq); winnerKSq = flop_square(winnerKSq);
loserKSq = flop_square(loserKSq); loserKSq = flop_square(loserKSq);
@@ -153,7 +165,7 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
+ distance_bonus(square_distance(winnerKSq, loserKSq)) + distance_bonus(square_distance(winnerKSq, loserKSq))
+ kbnk_mate_table(loserKSq); + kbnk_mate_table(loserKSq);
return (strongerSide == pos.side_to_move() ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -161,8 +173,8 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
template<> template<>
Value EvaluationFunction<KPK>::apply(const Position& pos) const { Value EvaluationFunction<KPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
@@ -198,7 +210,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) const {
+ PawnValueEndgame + PawnValueEndgame
+ Value(square_rank(wpsq)); + Value(square_rank(wpsq));
return (strongerSide == pos.side_to_move() ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -239,7 +251,7 @@ Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
// If the weaker side's king is too far from the pawn and the rook, // If the weaker side's king is too far from the pawn and the rook,
// it's a win // it's a win
else if ( square_distance(bksq, bpsq) - (tempo^1) >= 3 else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
&& square_distance(bksq, wrsq) >= 3) && square_distance(bksq, wrsq) >= 3)
result = RookValueEndgame - Value(square_distance(wksq, bpsq)); result = RookValueEndgame - Value(square_distance(wksq, bpsq));
@@ -257,7 +269,7 @@ Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
+ Value(square_distance(bksq, bpsq + DELTA_S) * 8) + Value(square_distance(bksq, bpsq + DELTA_S) * 8)
+ Value(square_distance(bpsq, queeningSq) * 8); + Value(square_distance(bpsq, queeningSq) * 8);
return (strongerSide == pos.side_to_move() ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -273,7 +285,7 @@ Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, BISHOP) == 1);
Value result = mate_table(pos.king_square(weakerSide)); Value result = mate_table(pos.king_square(weakerSide));
return (pos.side_to_move() == strongerSide ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -291,10 +303,12 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
Square defendingKSq = pos.king_square(weakerSide); Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0); Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
Value result = Value(10) + mate_table(defendingKSq) + int d = square_distance(defendingKSq, nSq);
krkn_king_knight_distance_penalty(square_distance(defendingKSq, nSq)); Value result = Value(10)
+ mate_table(defendingKSq)
+ krkn_king_knight_distance_penalty(d);
return (strongerSide == pos.side_to_move())? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -319,7 +333,7 @@ Value EvaluationFunction<KQKR>::apply(const Position& pos) const {
+ mate_table(loserKSq) + mate_table(loserKSq)
+ distance_bonus(square_distance(winnerKSq, loserKSq)); + distance_bonus(square_distance(winnerKSq, loserKSq));
return (strongerSide == pos.side_to_move())? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
template<> template<>
@@ -343,9 +357,9 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
result += Value(square_distance(bksq, nsq) * 32); result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility // Bonus for restricting the knight's mobility
result += Value((8 - count_1s_max_15(pos.attacks_from<KNIGHT>(nsq))) * 8); result += Value((8 - count_1s<CNT32_MAX15>(pos.attacks_from<KNIGHT>(nsq))) * 8);
return (strongerSide == pos.side_to_move() ? result : -result); return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -353,17 +367,17 @@ 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(0); return VALUE_DRAW;
} }
template<> template<>
Value EvaluationFunction<KNNK>::apply(const Position&) const { Value EvaluationFunction<KNNK>::apply(const Position&) const {
return Value(0); return VALUE_DRAW;
} }
/// KBPKScalingFunction scales endgames where the stronger side has king, /// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a /// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is /// bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used. /// will be used.
template<> template<>
@@ -387,7 +401,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8)); Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
if ( square_color(queeningSq) != square_color(bishopSq) if ( !same_color_squares(queeningSq, bishopSq)
&& file_distance(square_file(kingSq), pawnFile) <= 1) && file_distance(square_file(kingSq), pawnFile) <= 1)
{ {
// The bishop has the wrong color, and the defending king is on the // The bishop has the wrong color, and the defending king is on the
@@ -401,15 +415,15 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
} }
else else
{ {
for(rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {} for (rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {}
rank = Rank(rank^7); // HACK to get the relative rank rank = Rank(rank ^ 7); // HACK to get the relative rank
assert(rank >= RANK_2 && rank <= RANK_7); assert(rank >= RANK_2 && rank <= RANK_7);
} }
// If the defending king has distance 1 to the promotion square or // If the defending king has distance 1 to the promotion square or
// is placed somewhere in front of the pawn, it's a draw. // is placed somewhere in front of the pawn, it's a draw.
if ( square_distance(kingSq, queeningSq) <= 1 if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank) || relative_rank(strongerSide, kingSq) >= rank)
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -438,7 +452,7 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
{ {
Square rsq = pos.piece_list(weakerSide, ROOK, 0); Square rsq = pos.piece_list(weakerSide, ROOK, 0);
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide)) if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -495,7 +509,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5 && wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6))) && (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6)))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
// The defending side saves a draw by checking from behind in case the pawn // The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind. // has advanced to the 6th rank with the king behind.
@@ -503,13 +517,13 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6 && square_rank(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3))) && (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3)))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
if ( r >= RANK_6 if ( r >= RANK_6
&& bksq == queeningSq && bksq == queeningSq
&& square_rank(brsq) == RANK_1 && square_rank(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2)) && (!tempo || square_distance(wksq, wpsq) >= 2))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn. // and the black rook is behind the pawn.
@@ -518,7 +532,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& (bksq == SQ_H7 || bksq == SQ_G7) && (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A && square_file(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5)) && (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
// If the defending king blocks the pawn and the attacking king is too far // If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw. // away, it's a draw.
@@ -526,7 +540,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
&& bksq == wpsq + DELTA_N && bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2 && square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2) && square_distance(wksq, brsq) - tempo >= 2)
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
// Pawn on the 7th rank supported by the rook from behind usually wins if the // Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king, // attacking king is closer to the queening square than the defending king,
@@ -549,8 +563,8 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
|| ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX return ScaleFactor( SCALE_FACTOR_MAX
- (8 * square_distance(wpsq, queeningSq) - 8 * square_distance(wpsq, queeningSq)
+ 2 * square_distance(wksq, queeningSq))); - 2 * square_distance(wksq, queeningSq));
// If the pawn is not far advanced, and the defending king is somewhere in // If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw. // the pawn's path, it's probably a draw.
@@ -611,40 +625,32 @@ ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {
template<> template<>
ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2); assert(pos.piece_count(strongerSide, PAWN) >= 2);
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(PAWN, strongerSide); Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file? // Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB) if ((pawns & ~FileABB) == EmptyBoardBB)
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
Square ksq = pos.king_square(weakerSide); if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
if (square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1) || ( square_file(ksq) == FILE_A
return ScaleFactor(0); && (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
else if( square_file(ksq) == FILE_A return SCALE_FACTOR_ZERO;
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
} }
// Are all pawns on the 'h' file? // Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB) else if ((pawns & ~FileHBB) == EmptyBoardBB)
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
Square ksq = pos.king_square(weakerSide); if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
if (square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1) || ( square_file(ksq) == FILE_H
return ScaleFactor(0); && (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
else if ( square_file(ksq) == FILE_H return SCALE_FACTOR_ZERO;
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)
return ScaleFactor(0);
else
return SCALE_FACTOR_NONE;
} }
else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -672,12 +678,12 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( square_color(weakerKingSq) != square_color(strongerBishopSq) && ( !same_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
// Case 2: Opposite colored bishops // Case 2: Opposite colored bishops
if (square_color(strongerBishopSq) != square_color(weakerBishopSq)) if (!same_color_squares(strongerBishopSq, weakerBishopSq))
{ {
// We assume that the position is drawn in the following three situations: // We assume that the position is drawn in the following three situations:
// //
@@ -690,15 +696,17 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
// reasonably well. // reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5) if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return ScaleFactor(0); 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))
return ScaleFactor(0); if (path & pos.pieces(KING, weakerSide))
if( (pos.attacks_from<BISHOP>(weakerBishopSq) & ray) return SCALE_FACTOR_ZERO;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3) && square_distance(weakerBishopSq, pawnSq) >= 3)
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -720,7 +728,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0); Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0); Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
if (square_color(wbsq) == square_color(bbsq)) if (same_color_squares(wbsq, bbsq))
// Not opposite-colored bishops, no scaling // Not opposite-colored bishops, no scaling
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -749,8 +757,8 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// some square in the frontmost pawn's path. // some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1) if ( square_file(ksq) == square_file(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& square_color(ksq) != square_color(wbsq)) && !same_color_squares(ksq, wbsq))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -759,16 +767,17 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
// in front of the frontmost pawn's path, and the square diagonally behind // in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn. // this square on the file of the other pawn.
if ( ksq == blockSq1 if ( ksq == blockSq1
&& square_color(ksq) != square_color(wbsq) && !same_color_squares(ksq, wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide)) || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| rank_distance(r1, r2) >= 2)) || rank_distance(r1, r2) >= 2))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
else if ( ksq == blockSq2 else if ( ksq == blockSq2
&& square_color(ksq) != square_color(wbsq) && !same_color_squares(ksq, wbsq)
&& ( bbsq == blockSq1 && ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide)))) || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -799,9 +808,9 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( square_file(weakerKingSq) == square_file(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( square_color(weakerKingSq) != square_color(strongerBishopSq) && ( !same_color_squares(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -816,7 +825,7 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0); Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
@@ -824,11 +833,11 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
if ( pawnSq == relative_square(strongerSide, SQ_A7) if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
if ( pawnSq == relative_square(strongerSide, SQ_H7) if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return ScaleFactor(0); return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@@ -843,8 +852,8 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position& pos) const {
template<> template<>
ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const { ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1); assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1); assert(pos.piece_count(BLACK, PAWN) == 1);
@@ -881,32 +890,21 @@ ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a // Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, it's probably at least a draw even with the pawn. // draw, it's probably at least a draw even with the pawn.
if (probe_kpk(wksq, wpsq, bksq, stm)) return probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO;
return SCALE_FACTOR_NONE;
else
return ScaleFactor(0);
}
/// init_bitbases() is called during program initialization, and simply loads
/// 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.
void init_bitbases() {
generate_kpk_bitbase(KPKBitbase);
} }
namespace { namespace {
// Probe the KP vs K bitbase: // Probe the KP vs K bitbase
int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { int probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
int wp = int(square_file(wpsq)) + (int(square_rank(wpsq)) - 1) * 4; int wp = square_file(wpsq) + 4 * (square_rank(wpsq) - 1);
int index = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*wp; int index = int(stm) + 2 * bksq + 128 * wksq + 8192 * wp;
assert(index >= 0 && index < 24576*8); assert(index >= 0 && index < 24576 * 8);
return KPKBitbase[index/8] & (1 << (index&7));
return KPKBitbase[index / 8] & (1 << (index & 7));
} }
} }
-2
View File
@@ -26,7 +26,6 @@
//// ////
#include "position.h" #include "position.h"
#include "scale.h"
#include "value.h" #include "value.h"
@@ -102,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)
+449 -449
View File
File diff suppressed because it is too large Load Diff
+4 -80
View File
@@ -21,90 +21,14 @@
#if !defined(EVALUATE_H_INCLUDED) #if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
//// #include "color.h"
//// Includes #include "value.h"
////
#include <iostream>
#include "material.h"
#include "pawns.h"
////
//// Types
////
/// The EvalInfo struct contains various information computed and collected
/// by the evaluation function. An EvalInfo object is passed as one of the
/// arguments to the evaluation function, and the search can make use of its
/// contents to make intelligent search decisions.
///
/// At the moment, this is not utilized very much: The only part of the
/// EvalInfo object which is used by the search is futilityMargin.
class Position; class Position;
struct EvalInfo { extern Value evaluate(const Position& pos, Value& margin);
EvalInfo() { kingDanger[0] = kingDanger[1] = Value(0); }
// Middle game and endgame evaluations
Score value;
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
PawnInfo* pi;
// attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][0] contains
// all squares attacked by the given color.
Bitboard attackedBy[2][8];
Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
// kingZone[color] is the zone around the enemy king which is considered
// by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file)
// squares two ranks in front of the king. For instance, if black's king
// is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6.
Bitboard kingZone[2];
// kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingZone of the enemy king.
int kingAttackersCount[2];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingZone of the enemy king. The
// weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
int kingAttackersWeight[2];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
// directly adjacent to the king of the given color. Pieces which attack
// more than one square are counted multiple times. For instance, if black's
// king is on g8 and there's a white knight on g5, this knight adds
// 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[2];
// Middle game and endgame mobility scores
Score mobility;
// Value of the danger for the king of the given color
Value kingDanger[2];
};
////
//// Prototypes
////
extern Value evaluate(const Position& pos, EvalInfo& ei);
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)
+4 -21
View File
@@ -41,8 +41,8 @@ History::History() { clear(); }
/// History::clear() clears the history tables /// History::clear() clears the history tables
void History::clear() { void History::clear() {
memset(history, 0, 2 * 8 * 64 * sizeof(int)); memset(history, 0, 16 * 64 * sizeof(int));
memset(maxStaticValueDelta, 0, 2 * 8 * 64 * sizeof(int)); memset(maxStaticValueDelta, 0, 16 * 64 * sizeof(int));
} }
@@ -85,31 +85,14 @@ void History::failure(Piece p, Square to, Depth d) {
} }
/// History::move_ordering_score() returns an integer value used to order the
/// non-capturing moves in the MovePicker class.
int History::move_ordering_score(Piece p, Square to) const {
assert(piece_is_ok(p));
assert(square_is_ok(to));
return history[p][to];
}
/// History::set_gain() and History::gain() store and retrieve the /// History::set_gain() and History::gain() store and retrieve the
/// gain of a move given the delta of the static position evaluations /// gain of a move given the delta of the static position evaluations
/// before and after the move. /// before and after the move.
void History::set_gain(Piece p, Square to, Value delta) void History::set_gain(Piece p, Square to, Value delta) {
{
if (delta >= maxStaticValueDelta[p][to]) if (delta >= maxStaticValueDelta[p][to])
maxStaticValueDelta[p][to] = delta; maxStaticValueDelta[p][to] = delta;
else else
maxStaticValueDelta[p][to]--; maxStaticValueDelta[p][to]--;
} }
Value History::gain(Piece p, Square to) const
{
return Value(maxStaticValueDelta[p][to]);
}
+14 -2
View File
@@ -49,7 +49,7 @@ public:
void clear(); void clear();
void success(Piece p, Square to, Depth d); void success(Piece p, Square to, Depth d);
void failure(Piece p, Square to, Depth d); void failure(Piece p, Square to, Depth d);
int move_ordering_score(Piece p, Square to) const; int value(Piece p, Square to) const;
void set_gain(Piece p, Square to, Value delta); void set_gain(Piece p, Square to, Value delta);
Value gain(Piece p, Square to) const; Value gain(Piece p, Square to) const;
@@ -71,7 +71,19 @@ private:
/// recently have a bigger importance for move ordering than the moves which /// recently have a bigger importance for move ordering than the moves which
/// have been searched a long time ago. /// have been searched a long time ago.
const int HistoryMax = 50000 * OnePly; const int HistoryMax = 50000 * ONE_PLY;
////
//// Inline functions
////
inline int History::value(Piece p, Square to) const {
return history[p][to];
}
inline Value History::gain(Piece p, Square to) const {
return Value(maxStaticValueDelta[p][to]);
}
#endif // !defined(HISTORY_H_INCLUDED) #endif // !defined(HISTORY_H_INCLUDED)
+14 -56
View File
@@ -22,67 +22,21 @@
#define LOCK_H_INCLUDED #define LOCK_H_INCLUDED
// x86 assembly language locks or OS spin locks may perform faster than #if !defined(_MSC_VER)
// mutex locks on some platforms. On my machine, mutexes seem to be the
// best.
//#define ASM_LOCK
//#define OS_SPIN_LOCK
#if defined(ASM_LOCK)
typedef volatile int Lock;
static inline void LockX86(Lock *lock) {
int dummy;
asm __volatile__("1: movl $1, %0" "\n\t"
" xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jz 3f" "\n\t" "2: pause" "\n\t"
" movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
"\n\t":"=&q"(dummy)
:"q"(lock)
:"cc");
}
static inline void UnlockX86(Lock *lock) {
int dummy;
asm __volatile__("movl $0, (%1)":"=&q"(dummy)
:"q"(lock));
}
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) LockX86(x)
# define lock_release(x) UnlockX86(x)
# define lock_destroy(x)
#elif defined(OS_SPIN_LOCK)
# include <libkern/OSAtomic.h>
typedef OSSpinLock Lock;
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) OSSpinLockLock(x)
# define lock_release(x) OSSpinLockUnlock(x)
# define lock_destroy(x)
#elif !defined(_MSC_VER)
# 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, y) pthread_mutex_init(x, y) # 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
@@ -91,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;
# define lock_init(x, y) InitializeCriticalSection(x) typedef HANDLE WaitCondition;
# 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)
+39 -20
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;
} }
+50 -45
View File
@@ -23,7 +23,7 @@
//// ////
#include <cassert> #include <cassert>
#include <sstream> #include <cstring>
#include <map> #include <map>
#include "material.h" #include "material.h"
@@ -47,16 +47,18 @@ 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 } };
typedef EndgameEvaluationFunctionBase EF; typedef EndgameEvaluationFunctionBase EF;
typedef EndgameScalingFunctionBase SF; typedef EndgameScalingFunctionBase SF;
typedef map<Key, EF*> EFMap;
typedef map<Key, SF*> SFMap;
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key. // the function maps because correspond to more then one material hash key.
@@ -70,7 +72,7 @@ namespace {
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == Value(0) return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0 && pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame; && pos.non_pawn_material(Us) >= RookValueMidgame;
} }
@@ -113,21 +115,17 @@ private:
static Key buildKey(const string& keyCode); static Key buildKey(const string& keyCode);
static const string swapColors(const string& keyCode); static const string swapColors(const string& keyCode);
// Here we store two maps, for evaluate and scaling functions // Here we store two maps, for evaluate and scaling functions...
pair<map<Key, EF*>, map<Key, SF*> > maps; pair<EFMap, SFMap> maps;
// Maps accessing functions returning const and non-const references // ...and here is the accessing template function
template<typename T> const map<Key, T*>& get() const { return maps.first; } template<typename T> const map<Key, T*>& get() const;
template<typename T> map<Key, T*>& get() { return maps.first; }
}; };
// Explicit specializations of a member function shall be declared in // Explicit specializations of a member function shall be declared in
// the namespace of which the class template is a member. // the namespace of which the class template is a member.
template<> const map<Key, SF*>& template<> const EFMap& EndgameFunctions::get<EF>() const { return maps.first; }
EndgameFunctions::get<SF>() const { return maps.second; } template<> const SFMap& EndgameFunctions::get<SF>() const { return maps.second; }
template<> map<Key, SF*>&
EndgameFunctions::get<SF>() { return maps.second; }
//// ////
@@ -136,18 +134,18 @@ EndgameFunctions::get<SF>() { return maps.second; }
/// MaterialInfoTable c'tor and d'tor, called once by each thread /// MaterialInfoTable c'tor and d'tor, called once by each thread
MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) { MaterialInfoTable::MaterialInfoTable() {
size = numOfEntries; entries = new MaterialInfo[MaterialTableSize];
entries = new MaterialInfo[size];
funcs = new EndgameFunctions(); funcs = new EndgameFunctions();
if (!entries || !funcs) if (!entries || !funcs)
{ {
cerr << "Failed to allocate " << numOfEntries * 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() {
@@ -167,7 +165,8 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
if (npm >= MidgameLimit) if (npm >= MidgameLimit)
return PHASE_MIDGAME; return PHASE_MIDGAME;
else if (npm <= EndgameLimit)
if (npm <= EndgameLimit)
return PHASE_ENDGAME; return PHASE_ENDGAME;
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
@@ -182,7 +181,7 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) { MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
Key key = pos.get_material_key(); Key key = pos.get_material_key();
int index = key & (size - 1); unsigned index = unsigned(key & (MaterialTableSize - 1));
MaterialInfo* mi = entries + index; MaterialInfo* mi = entries + index;
// If mi->key matches the position's material hash key, it means that we // If mi->key matches the position's material hash key, it means that we
@@ -192,7 +191,8 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
return mi; return mi;
// Clear the MaterialInfo object, and set its key // Clear the MaterialInfo object, and set its key
mi->clear(); memset(mi, 0, sizeof(MaterialInfo));
mi->factor[WHITE] = mi->factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
mi->key = key; mi->key = key;
// Store game phase // Store game phase
@@ -204,12 +204,13 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL) if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL)
return mi; return mi;
else if (is_KXK<WHITE>(pos) || is_KXK<BLACK>(pos)) if (is_KXK<WHITE>(pos) || is_KXK<BLACK>(pos))
{ {
mi->evaluationFunction = is_KXK<WHITE>(pos) ? &EvaluateKXK[WHITE] : &EvaluateKXK[BLACK]; mi->evaluationFunction = is_KXK<WHITE>(pos) ? &EvaluateKXK[WHITE] : &EvaluateKXK[BLACK];
return mi; return mi;
} }
else if ( pos.pieces(PAWN) == EmptyBoardBB
if ( pos.pieces(PAWN) == EmptyBoardBB
&& pos.pieces(ROOK) == EmptyBoardBB && pos.pieces(ROOK) == EmptyBoardBB
&& pos.pieces(QUEEN) == EmptyBoardBB) && pos.pieces(QUEEN) == EmptyBoardBB)
{ {
@@ -254,7 +255,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
else if (is_KQKRPs<BLACK>(pos)) else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0)) if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == VALUE_ZERO)
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (pos.piece_count(BLACK, PAWN) == 0)
{ {
@@ -288,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) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
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;
@@ -332,9 +335,9 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
// //
// We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece", // We use PIECE_TYPE_NONE as a place holder for the bishop pair "extended piece",
// this allow us to be more flexible in defining bishop pair bonuses. // this allow us to be more flexible in defining bishop pair bonuses.
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++)
{ {
pc = pieceCount[c][pt1]; pc = pieceCount[c][pt1];
if (!pc) if (!pc)
@@ -342,7 +345,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
vv = LinearCoefficients[pt1]; vv = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++)
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2] vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2]; + pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
@@ -355,7 +358,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
} }
/// EndgameFunctions member definitions. /// EndgameFunctions member definitions
EndgameFunctions::EndgameFunctions() { EndgameFunctions::EndgameFunctions() {
@@ -378,19 +381,19 @@ EndgameFunctions::EndgameFunctions() {
EndgameFunctions::~EndgameFunctions() { EndgameFunctions::~EndgameFunctions() {
for (map<Key, EF*>::iterator it = maps.first.begin(); it != maps.first.end(); ++it) for (EFMap::const_iterator it = maps.first.begin(); it != maps.first.end(); ++it)
delete (*it).second; delete it->second;
for (map<Key, SF*>::iterator it = maps.second.begin(); it != maps.second.end(); ++it) for (SFMap::const_iterator it = maps.second.begin(); it != maps.second.end(); ++it)
delete (*it).second; delete it->second;
} }
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
@@ -400,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);
} }
@@ -417,14 +421,15 @@ template<class T>
void EndgameFunctions::add(const string& keyCode) { void EndgameFunctions::add(const string& keyCode) {
typedef typename T::Base F; typedef typename T::Base F;
typedef map<Key, F*> M;
get<F>().insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE))); const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE)));
get<F>().insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK))); const_cast<M&>(get<F>()).insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK)));
} }
template<class T> template<class T>
T* EndgameFunctions::get(Key key) const { T* EndgameFunctions::get(Key key) const {
typename map<Key, T*>::const_iterator it(get<T>().find(key)); typename map<Key, T*>::const_iterator it = get<T>().find(key);
return (it != get<T>().end() ? it->second : NULL); return it != get<T>().end() ? it->second : NULL;
} }
+6 -21
View File
@@ -27,13 +27,14 @@
#include "endgame.h" #include "endgame.h"
#include "position.h" #include "position.h"
#include "scale.h"
//// ////
//// Types //// Types
//// ////
const int MaterialTableSize = 1024;
/// MaterialInfo is a class which contains various information about a /// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation, /// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in /// a function pointer to a special endgame evaluation function (which in
@@ -49,8 +50,6 @@ class MaterialInfo {
friend class MaterialInfoTable; friend class MaterialInfoTable;
public: public:
MaterialInfo() : key(0) { clear(); }
Score material_value() const; Score material_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const; ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const; int space_weight() const;
@@ -59,8 +58,6 @@ public:
Value evaluate(const Position& pos) const; Value evaluate(const Position& pos) const;
private: private:
inline void clear();
Key key; Key key;
int16_t value; int16_t value;
uint8_t factor[2]; uint8_t factor[2];
@@ -78,15 +75,17 @@ class EndgameFunctions;
class MaterialInfoTable { class MaterialInfoTable {
MaterialInfoTable(const MaterialInfoTable&);
MaterialInfoTable& operator=(const MaterialInfoTable&);
public: public:
MaterialInfoTable(unsigned numOfEntries); MaterialInfoTable();
~MaterialInfoTable(); ~MaterialInfoTable();
MaterialInfo* get_material_info(const Position& pos); MaterialInfo* get_material_info(const Position& pos);
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
private: private:
unsigned size;
MaterialInfo* entries; MaterialInfo* entries;
EndgameFunctions* funcs; EndgameFunctions* funcs;
}; };
@@ -105,20 +104,6 @@ inline Score MaterialInfo::material_value() const {
return make_score(value, value); return make_score(value, value);
} }
/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
/// with all slots at their default values but the key.
inline void MaterialInfo::clear() {
value = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
spaceWeight = 0;
}
/// MaterialInfo::scale_factor takes a position and a color as input, and /// MaterialInfo::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the /// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not /// position in addition to the color, because the scale factor need not
-171
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);
}
+40 -67
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.8 beta 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,10 +67,8 @@ static const string AppTag = "";
//// Variables //// Variables
//// ////
bool Chess960; static uint64_t dbg_cnt0 = 0;
static uint64_t dbg_cnt1 = 0;
uint64_t dbg_cnt0 = 0;
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;
@@ -129,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 cpu64(CpuHas64BitPath ? " 64bit" : ""); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
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();
} }
@@ -220,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);
@@ -241,46 +224,36 @@ 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)) // If we're running under XBoard then we can't use PeekConsoleInput() as
return 1; // the input commands are sent to us directly over the internal pipe.
return dw; if (usePipe)
} else { return PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL) ? dw : 1;
// Count the number of unread input records, including keyboard, // Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records. // mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &dw); GetNumberOfConsoleInputEvents(inh, &dw);
if (dw <= 0)
return 0;
// Read data from console without removing it from the buffer // Read data from console without removing it from the buffer
INPUT_RECORD rec[256]; if (dw <= 0 || !PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
DWORD recCnt;
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
return 0; return 0;
// Search for at least one keyboard event // Search for at least one keyboard event
@@ -289,11 +262,11 @@ int Bioskey()
return 1; return 1;
return 0; 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.
+2 -14
View File
@@ -29,7 +29,6 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "application.h"
#include "types.h" #include "types.h"
//// ////
@@ -40,13 +39,6 @@
#define Max(x, y) (((x) < (y))? (y) : (x)) #define Max(x, y) (((x) < (y))? (y) : (x))
////
//// Variables
////
extern bool Chess960;
//// ////
//// Prototypes //// Prototypes
//// ////
@@ -54,8 +46,9 @@ extern bool Chess960;
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);
//// ////
@@ -65,9 +58,6 @@ extern void prefetch(char* addr);
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();
@@ -75,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)
+32 -32
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':
@@ -71,44 +72,44 @@ Move move_from_string(const Position& pos, const std::string& str) {
} }
} }
// 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) { 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);
@@ -120,17 +121,15 @@ const std::string move_to_string(Move move) {
str = "0000"; str = "0000";
else else
{ {
if (!Chess960) if (move_is_short_castle(move) && !chess960)
{
if (move_is_short_castle(move))
return (from == SQ_E1 ? "e1g1" : "e8g8"); return (from == SQ_E1 ? "e1g1" : "e8g8");
if (move_is_long_castle(move)) if (move_is_long_castle(move) && !chess960)
return (from == SQ_E1 ? "e1c1" : "e8c8"); return (from == SQ_E1 ? "e1c1" : "e8c8");
}
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;
} }
@@ -138,9 +137,10 @@ const std::string move_to_string(Move move) {
/// Overload the << operator, to make it easier to print moves. /// Overload the << operator, to make it easier to print moves.
std::ostream &operator << (std::ostream& os, Move m) { std::ostream& operator << (std::ostream& os, Move m) {
return os << move_to_string(m); bool chess960 = (os.iword(0) != 0); // See set960()
return os << move_to_uci(m, chess960);
} }
+14 -13
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;
@@ -201,9 +202,9 @@ inline Move make_ep_move(Square from, Square to) {
//// Prototypes //// Prototypes
//// ////
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); 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);
+123 -121
View File
@@ -140,6 +140,32 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
} }
/// generate_non_evasions() generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Color us = pos.side_to_move();
Bitboard target = pos.pieces_of_color(opposite_color(us));
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
}
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
@@ -218,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);
@@ -260,11 +286,8 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
// Generate pseudo-legal moves // Generate pseudo-legal moves
if (pos.is_check()) last = pos.is_check() ? generate_evasions(pos, mlist)
last = generate_evasions(pos, mlist); : generate_non_evasions(pos, mlist);
else
last = generate_noncaptures(pos, generate_captures(pos, mlist));
if (pseudoLegal) if (pseudoLegal)
return last; return last;
@@ -285,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++)
@@ -312,7 +335,7 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
Piece pc = pos.piece_on(from); Piece pc = pos.piece_on(from);
// Use a slower but simpler function for uncommon cases // Use a slower but simpler function for uncommon cases
if (move_is_ep(m) || move_is_castle(m)) if (move_is_special(m))
return move_is_legal(pos, m); return move_is_legal(pos, m);
// If the from square is not occupied by a piece belonging to the side to // If the from square is not occupied by a piece belonging to the side to
@@ -332,14 +355,9 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
if ((us == WHITE) != (direction > 0)) if ((us == WHITE) != (direction > 0))
return false; return false;
// A pawn move is a promotion iff the destination square is // We have already handled promotion moves, so destination
// on the 8/1th rank. // cannot be on the 8/1th rank.
if (( (square_rank(to) == RANK_8 && us == WHITE) if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1)
||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m)))
return false;
// The promotion piece, if any, must be valid
if (move_promotion_piece(m) > QUEEN || move_promotion_piece(m) == PAWN)
return false; return false;
// Proceed according to the square delta between the origin and // Proceed according to the square delta between the origin and
@@ -386,14 +404,12 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
default: default:
return false; return false;
} }
}
else if (!bit_is_set(pos.attacks_from(pc, from), to))
return false;
// The move is pseudo-legal, check if it is also legal // The move is pseudo-legal, check if it is also legal
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned); return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
}
// Luckly we can handle all the other pieces in one go
return bit_is_set(pos.attacks_from(pc, from), to)
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
&& !move_is_promotion(m);
} }
@@ -406,10 +422,13 @@ namespace {
Square from; Square from;
const Square* ptr = pos.piece_list_begin(us, Piece); const Square* ptr = pos.piece_list_begin(us, Piece);
while ((from = *ptr++) != SQ_NONE) if (*ptr != SQ_NONE)
{ {
do {
from = *ptr;
b = pos.attacks_from<Piece>(from) & target; b = pos.attacks_from<Piece>(from) & target;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
} }
return mlist; return mlist;
} }
@@ -425,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)
{
to = pop_1st_bit(&b2);
if (Type == CAPTURE || Type == EVASION) if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN); (*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION) if (Type == NON_CAPTURE || Type == EVASION)
{ {
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK); (*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP); (*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); (*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
} }
// This is the only possible under promotion that can give a check // This is the only possible under promotion that can give a check
// not already included in the queen-promotion. It is not sure that // not already included in the queen-promotion.
// the promoted knight will give check, but it doesn't worth to verify. if ( Type == CHECK
if (Type == CHECK) && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us))))
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); (*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
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const 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
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
// Filter out unwanted pushes according to the move type
if (Type == EVASION) if (Type == EVASION)
{ {
b1 &= target; emptySquares &= target; // Only blocking squares
b2 &= target; enemyPieces &= target; // Capture only the checker piece
} }
else if (Type == CHECK)
// Promotions and underpromotions
if (pawnsOn7)
{ {
// Pawn moves which give direct cheks 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)
{
// Condider only pawn moves which give direct checks
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;
+1
View File
@@ -36,6 +36,7 @@ extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist); extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist); extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist); extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false); extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned); extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
extern bool move_is_legal(const Position& pos, const Move m); extern bool move_is_legal(const Position& pos, const Move m);
+15 -26
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;
lastBadCapture = badCaptures; badCaptures = moves + MOVES_MAX;
pinned = p.pinned_pieces(pos.side_to_move()); pinned = p.pinned_pieces(pos.side_to_move());
@@ -90,16 +90,16 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
if (p.is_check()) if (p.is_check())
phasePtr = EvasionsPhaseTable; phasePtr = EvasionsPhaseTable;
else if (d > Depth(0)) else if (d > DEPTH_ZERO)
{ {
// Consider sligtly negative captures as good if at low // Consider sligtly negative captures as good if at low
// depth and far from beta. // depth and far from beta.
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * OnePly) if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
badCaptureThreshold = -PawnValueMidgame; badCaptureThreshold = -PawnValueMidgame;
phasePtr = MainSearchPhaseTable; phasePtr = MainSearchPhaseTable;
} }
else if (d == Depth(0)) else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksPhaseTable; phasePtr = QsearchWithChecksPhaseTable;
else else
{ {
@@ -148,16 +148,16 @@ void MovePicker::go_next_phase() {
return; return;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just sort them // Bad captures SEE value is already calculated so just pick
// to get SEE move ordering. // them in order to get SEE move ordering.
curMove = badCaptures; curMove = badCaptures;
lastMove = lastBadCapture; lastMove = moves + MOVES_MAX;
return; return;
case PH_EVASIONS: case PH_EVASIONS:
assert(pos.is_check()); assert(pos.is_check());
lastMove = generate_evasions(pos, moves); lastMove = generate_evasions(pos, moves);
score_evasions_or_checks(); score_evasions();
return; return;
case PH_QCAPTURES: case PH_QCAPTURES:
@@ -167,7 +167,6 @@ void MovePicker::go_next_phase() {
case PH_QCHECKS: case PH_QCHECKS:
lastMove = generate_non_capture_checks(pos, moves); lastMove = generate_non_capture_checks(pos, moves);
score_evasions_or_checks();
return; return;
case PH_STOP: case PH_STOP:
@@ -221,7 +220,6 @@ void MovePicker::score_noncaptures() {
Move m; Move m;
Piece piece; Piece piece;
Square from, to; Square from, to;
int hs;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
@@ -229,18 +227,11 @@ void MovePicker::score_noncaptures() {
from = move_from(m); from = move_from(m);
to = move_to(m); to = move_to(m);
piece = pos.piece_on(from); piece = pos.piece_on(from);
hs = H.move_ordering_score(piece, to); cur->score = H.value(piece, to) + H.gain(piece, to);
// Ensure history has always highest priority
if (hs > 0)
hs += 10000;
// Gain table based scoring
cur->score = hs + 16 * H.gain(piece, to);
} }
} }
void MovePicker::score_evasions_or_checks() { void MovePicker::score_evasions() {
// Try good captures ordered by MVV/LVA, then non-captures if // Try good captures ordered by MVV/LVA, then non-captures if
// destination square is not under attack, ordered by history // destination square is not under attack, ordered by history
// value, and at the end bad-captures and non-captures with a // value, and at the end bad-captures and non-captures with a
@@ -261,7 +252,7 @@ void MovePicker::score_evasions_or_checks() {
cur->score = pos.midgame_value_of_piece_on(move_to(m)) cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + HistoryMax; - pos.type_of_piece_on(move_from(m)) + HistoryMax;
else else
cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m)); cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
} }
} }
@@ -301,12 +292,10 @@ Move MovePicker::get_next_move() {
if (seeValue >= badCaptureThreshold) if (seeValue >= badCaptureThreshold)
return move; return move;
// Losing capture, move it to the badCaptures[] array, note // Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality. // that move has now been already checked for legality.
assert(int(lastBadCapture - badCaptures) < 63); (--badCaptures)->move = move;
lastBadCapture->move = move; badCaptures->score = seeValue;
lastBadCapture->score = seeValue;
lastBadCapture++;
} }
break; break;
@@ -324,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
+3 -3
View File
@@ -56,7 +56,7 @@ public:
private: private:
void score_captures(); void score_captures();
void score_noncaptures(); void score_noncaptures();
void score_evasions_or_checks(); void score_evasions();
void go_next_phase(); void go_next_phase();
const Position& pos; const Position& pos;
@@ -65,8 +65,8 @@ private:
MoveStack ttMoves[2], killers[2]; MoveStack ttMoves[2], killers[2];
int badCaptureThreshold, phase; int badCaptureThreshold, phase;
const uint8_t* phasePtr; const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *lastBadCapture; MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack moves[256], badCaptures[64]; MoveStack moves[MOVES_MAX];
}; };
+57 -168
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] = {
@@ -70,36 +73,6 @@ namespace {
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
// Pawn storm tables for positions with opposite castling
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22,-22,-22,-14,-6, 0, 0, 0,
-6,-10,-10,-10,-6, 0, 0, 0,
4, 12, 16, 12, 4, 0, 0, 0,
16, 23, 23, 16, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,-10,-19,-28,-33,-33,
0, 0, 0,-10,-15,-19,-24,-24,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 10, 19, 19,
0, 0, 0, 0, 1, 19, 31, 27,
0, 0, 0, 0, 0, 22, 31, 22,
0, 0, 0, 0, 0, 0, 0, 0
};
// Pawn storm open file bonuses by file
const int16_t QStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
const int16_t KStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
// Pawn storm lever bonuses by file
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
#undef S #undef S
} }
@@ -110,15 +83,17 @@ namespace {
/// PawnInfoTable c'tor and d'tor instantiated one each thread /// PawnInfoTable c'tor and d'tor instantiated one each thread
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) : size(numOfEntries) { PawnInfoTable::PawnInfoTable() {
entries = new PawnInfo[PawnTableSize];
entries = new PawnInfo[size];
if (!entries) if (!entries)
{ {
std::cerr << "Failed to allocate " << (numOfEntries * 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));
} }
@@ -128,16 +103,6 @@ PawnInfoTable::~PawnInfoTable() {
} }
/// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// kingSquares[] is initialized to SQ_NONE instead.
void PawnInfo::clear() {
memset(this, 0, sizeof(PawnInfo));
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes /// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also stored /// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in a hash table, so we don't have to recompute everything when the same /// in a hash table, so we don't have to recompute everything when the same
@@ -148,7 +113,7 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
assert(pos.is_ok()); assert(pos.is_ok());
Key key = pos.get_pawn_key(); Key key = pos.get_pawn_key();
int index = int(key & (size - 1)); unsigned index = unsigned(key & (PawnTableSize - 1));
PawnInfo* pi = entries + index; PawnInfo* pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we // If pi->key matches the position's pawn hash key, it means that we
@@ -158,18 +123,20 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
return pi; return pi;
// Clear the PawnInfo object, and set the key // Clear the PawnInfo object, and set the key
pi->clear(); memset(pi, 0, sizeof(PawnInfo));
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
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;
} }
@@ -183,58 +150,44 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Square s; Square s;
File f; File f;
Rank r; Rank r;
int bonus;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = make_score(0, 0); Score value = SCORE_ZERO;
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 pawn storm scores by giving bonuses for open files
for (f = FILE_A; f <= FILE_H; f++)
if (!(ourPawns & file_bb(f)))
{
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
pi->qsStormValue[Us] += QStormOpenFileBonus[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)
{ {
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
f = square_file(s); f = square_file(s);
r = square_rank(s); r = square_rank(s);
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN)); // This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f);
// Calculate kingside and queenside pawn storm scores for both colors to be
// used when evaluating middle game positions with opposite side castling.
bonus = (f >= FILE_F ? evaluate_pawn_storm<Us, KingSide>(s, r, f, theirPawns) : 0);
pi->ksStormValue[Us] += KStormTable[relative_square(Us, s)] + bonus;
bonus = (f <= FILE_C ? evaluate_pawn_storm<Us, QueenSide>(s, r, f, theirPawns) : 0);
pi->qsStormValue[Us] += QStormTable[relative_square(Us, s)] + bonus;
// Our rank plus previous one. Used for chain detection. // Our rank plus previous one. Used for chain detection.
b = rank_bb(r) | rank_bb(r + (Us == WHITE ? -1 : 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;
// Test for backward pawn // Test for backward pawn
// //
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain // If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if // it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot // there are friendly pawns behind on neighboring files it cannot
// be backward either. // be backward either.
if ( (passed | isolated | chain) if ( !(passed | isolated | chain)
|| (ourPawns & attack_span_mask(opposite_color(Us), s)) && !(ourPawns & attack_span_mask(opposite_color(Us), s))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns)) && !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is // pawn on neighboring files. We now check whether the pawn is
@@ -257,93 +210,29 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// Test for candidate passed pawn // Test for candidate passed pawn
candidate = !(opposed | passed) candidate = !(opposed | passed)
&& (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_max_15(b) >= count_1s_max_15(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.
set_bit(&(pi->passedPawns), s); if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s);
// Score this pawn // Score this pawn
if (isolated) if (isolated)
{ value -= IsolatedPawnPenalty[opposed][f];
value -= IsolatedPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
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 (!(theirPawns & file_bb(f)))
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;
} }
/// PawnInfoTable::evaluate_pawn_storm() evaluates each pawn which seems
/// to have good chances of creating an open file by exchanging itself
/// against an enemy pawn on an adjacent file.
template<Color Us, PawnInfoTable::SideType Side>
int PawnInfoTable::evaluate_pawn_storm(Square s, Rank r, File f, Bitboard theirPawns) const {
const Bitboard StormFilesBB = (Side == KingSide ? FileFBB | FileGBB | FileHBB
: FileABB | FileBBB | FileCBB);
const int K = (Side == KingSide ? 2 : 4);
const File RookFile = (Side == KingSide ? FILE_H : FILE_A);
Bitboard b = attack_span_mask(Us, s) & theirPawns & StormFilesBB;
int bonus = 0;
while (b)
{
// Give a bonus according to the distance of the nearest enemy pawn
Square s2 = pop_1st_bit(&b);
Rank r2 = square_rank(s2);
int v = StormLeverBonus[f] - K * rank_distance(r, r2);
// If enemy pawn has no pawn beside itself is particularly vulnerable.
// Big bonus, especially against a weakness on the rook file
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
v *= (square_file(s2) == RookFile ? 4 : 2);
bonus += v;
}
return bonus;
}
/// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned shelter = 0;
unsigned r = ksq & (7 << 3);
for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
{
r += k;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
kingSquares[c] = ksq;
kingShelters[c] = shelter;
return shelter;
}
+54 -31
View File
@@ -26,49 +26,47 @@
//// ////
#include "bitboard.h" #include "bitboard.h"
#include "position.h"
#include "value.h" #include "value.h"
//// ////
//// Types //// Types
//// ////
const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn /// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game /// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table /// to add further information in the future. A lookup to the pawn hash table
/// (performed by calling the get_pawn_info method in a PawnInfoTable object) /// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object. /// returns a pointer to a PawnInfo object.
class Position;
class PawnInfo { class PawnInfo {
friend class PawnInfoTable; friend class PawnInfoTable;
public: public:
PawnInfo() { clear(); }
Score pawns_value() const; Score pawns_value() const;
Value kingside_storm_value(Color c) const;
Value queenside_storm_value(Color c) const;
Bitboard pawn_attacks(Color c) const; Bitboard pawn_attacks(Color c) const;
Bitboard passed_pawns() const; Bitboard passed_pawns(Color c) const;
int file_is_half_open(Color c, File f) const; int file_is_half_open(Color c, File f) const;
int has_open_file_to_left(Color c, File f) const; int has_open_file_to_left(Color c, File f) const;
int has_open_file_to_right(Color c, File f) const; int has_open_file_to_right(Color c, File f) const;
int get_king_shelter(const Position& pos, Color c, Square ksq);
template<Color Us>
Score king_shelter(const Position& pos, Square ksq);
private: private:
void clear(); template<Color Us>
int updateShelter(const Position& pos, Color c, Square ksq); Score updateShelter(const Position& pos, Square ksq);
Key key; Key key;
Bitboard passedPawns; Bitboard passedPawns[2];
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[2];
Square kingSquares[2]; Square kingSquares[2];
Score value; Score value;
int16_t ksStormValue[2], qsStormValue[2]; int halfOpenFiles[2];
uint8_t halfOpenFiles[2]; Score kingShelters[2];
uint8_t kingShelters[2];
}; };
/// The PawnInfoTable class represents a pawn hash table. It is basically /// The PawnInfoTable class represents a pawn hash table. It is basically
@@ -80,19 +78,19 @@ class PawnInfoTable {
enum SideType { KingSide, QueenSide }; enum SideType { KingSide, QueenSide };
PawnInfoTable(const PawnInfoTable&);
PawnInfoTable& operator=(const PawnInfoTable&);
public: public:
PawnInfoTable(unsigned numOfEntries); PawnInfoTable();
~PawnInfoTable(); ~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos) const; PawnInfo* get_pawn_info(const Position& pos) const;
void prefetch(Key key) const;
private: private:
template<Color Us> template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi) const; Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi) const;
template<Color Us, SideType Side>
int evaluate_pawn_storm(Square s, Rank r, File f, Bitboard theirPawns) const;
unsigned size;
PawnInfo* entries; PawnInfo* entries;
}; };
@@ -101,24 +99,23 @@ private:
//// Inline functions //// Inline functions
//// ////
inline Score PawnInfo::pawns_value() const { inline void PawnInfoTable::prefetch(Key key) const {
return value;
unsigned index = unsigned(key & (PawnTableSize - 1));
PawnInfo* pi = entries + index;
::prefetch((char*) pi);
} }
inline Bitboard PawnInfo::passed_pawns() const { inline Score PawnInfo::pawns_value() const {
return passedPawns; return value;
} }
inline Bitboard PawnInfo::pawn_attacks(Color c) const { inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c]; return pawnAttacks[c];
} }
inline Value PawnInfo::kingside_storm_value(Color c) const { inline Bitboard PawnInfo::passed_pawns(Color c) const {
return Value(ksStormValue[c]); return passedPawns[c];
}
inline Value PawnInfo::queenside_storm_value(Color c) const {
return Value(qsStormValue[c]);
} }
inline int PawnInfo::file_is_half_open(Color c, File f) const { inline int PawnInfo::file_is_half_open(Color c, File f) const {
@@ -133,8 +130,34 @@ inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
} }
inline int PawnInfo::get_king_shelter(const Position& pos, Color c, Square ksq) { /// PawnInfo::updateShelter() calculates and caches king shelter. It is called
return (kingSquares[c] == ksq ? kingShelters[c] : updateShelter(pos, c, ksq)); /// only when king square changes, about 20% of total king_shelter() calls.
template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8);
Bitboard pawns;
int r, shelter = 0;
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq);
r = square_rank(ksq) * 8;
for (int i = 1; i < 4; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
template<Color Us>
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // !defined(PAWNS_H_INCLUDED)
-49
View File
@@ -1,49 +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;
////
//// 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) : NO_PIECE_TYPE;
}
+35 -31
View File
@@ -25,8 +25,11 @@
//// Includes //// Includes
//// ////
#include <string>
#include "color.h" #include "color.h"
#include "square.h" #include "square.h"
#include "value.h"
//// ////
@@ -34,39 +37,45 @@
//// ////
enum PieceType { enum PieceType {
NO_PIECE_TYPE = 0, PIECE_TYPE_NONE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
}; };
enum Piece { enum Piece {
NO_PIECE = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6, PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16
EMPTY = 16, OUTSIDE = 17
}; };
ENABLE_OPERATORS_ON(PieceType);
ENABLE_OPERATORS_ON(Piece);
//// ////
//// Constants //// Constants
//// ////
const int SlidingArray[18] = { /// Important: If the material values are changed, one must also
0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0 /// adjust the piece square tables, and the method game_phase() in the
}; /// Position class!
///
/// Values modified by Joona Kiiski
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
//// ////
//// Inline functions //// Inline functions
//// ////
inline Piece operator+ (Piece p, int i) { return Piece(int(p) + i); }
inline void operator++ (Piece &p, int) { p = Piece(int(p) + 1); }
inline Piece operator- (Piece p, int i) { return Piece(int(p) - i); }
inline void operator-- (Piece &p, int) { p = Piece(int(p) - 1); }
inline PieceType operator+ (PieceType p, int i) {return PieceType(int(p) + i);}
inline void operator++ (PieceType &p, int) { p = PieceType(int(p) + 1); }
inline PieceType operator- (PieceType p, int i) {return PieceType(int(p) - i);}
inline void operator-- (PieceType &p, int) { p = PieceType(int(p) - 1); }
inline PieceType type_of_piece(Piece p) { inline PieceType type_of_piece(Piece p) {
return PieceType(int(p) & 7); return PieceType(int(p) & 7);
} }
@@ -79,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[int(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)
+438 -422
View File
File diff suppressed because it is too large Load Diff
+57 -39
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"
@@ -50,9 +37,6 @@
//// Constants //// Constants
//// ////
/// FEN string for the initial position
const std::string StartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/// Maximum number of plies per game (220 should be enough, because the /// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the /// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move). /// move counter for every non-reversible move).
@@ -68,7 +52,7 @@ const int MaxGameLength = 220;
struct CheckInfo { struct CheckInfo {
CheckInfo(const Position&); explicit CheckInfo(const Position&);
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard checkSq[8];
@@ -78,7 +62,7 @@ struct CheckInfo {
/// Castle rights, encoded as bit fields /// Castle rights, encoded as bit fields
enum CastleRights { enum CastleRights {
NO_CASTLES = 0, CASTLES_NONE = 0,
WHITE_OO = 1, WHITE_OO = 1,
BLACK_OO = 2, BLACK_OO = 2,
WHITE_OOO = 4, WHITE_OOO = 4,
@@ -105,7 +89,7 @@ struct StateInfo {
Score value; Score value;
Value npMaterial[2]; Value npMaterial[2];
PieceType capture; PieceType capturedType;
Key key; Key key;
Bitboard checkersBB; Bitboard checkersBB;
StateInfo* previous; StateInfo* previous;
@@ -149,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);
@@ -213,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;
@@ -227,7 +211,7 @@ public:
bool move_attacks_square(Move m, Square s) const; bool move_attacks_square(Move m, Square s) const;
// Piece captured with previous moves // Piece captured with previous moves
PieceType captured_piece() const; PieceType captured_piece_type() const;
// Information about pawns // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
@@ -246,7 +230,6 @@ public:
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const; int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see(Square to) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
@@ -258,27 +241,33 @@ public:
// Incremental evaluation // Incremental evaluation
Score value() const; Score value() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Score pst_delta(Piece piece, Square from, Square to) const; static Score pst_delta(Piece piece, Square from, Square to);
// Game termination checks // Game termination checks
bool is_mate() const; bool is_mate() const;
bool is_draw() const; bool is_draw() const;
// Check if one side threatens a mate in one // Check if side to move could be mated in one
bool has_mate_threat(Color c); bool has_mate_threat();
// Number of plies since the last non-reversible move // Number of plies since the last non-reversible move
int rule_50_counter() const; int rule_50_counter() const;
int startpos_ply_counter() const;
// Other properties of the position // Other properties of the position
bool opposite_colored_bishops() const; bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const; bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
// Current thread ID searching on the position // Current thread ID searching on the position
int thread() const; int thread() const;
// 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();
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;
@@ -292,8 +281,9 @@ 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);
// Helper functions for doing and undoing moves // Helper functions for doing and undoing moves
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep); void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep);
@@ -310,7 +300,7 @@ private:
Key compute_material_key() const; Key compute_material_key() const;
// Computing incremental evaluation scores and material counts // Computing incremental evaluation scores and material counts
Score pst(Color c, PieceType pt, Square s) const; static Score pst(Color c, PieceType pt, Square s);
Score compute_value() const; Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
@@ -333,7 +323,10 @@ private:
int castleRightsMask[64]; int castleRightsMask[64];
StateInfo startState; StateInfo startState;
File initialKFile, initialKRFile, initialQRFile; File initialKFile, initialKRFile, initialQRFile;
bool isChess960;
int startPosPlyCounter;
int threadID; int threadID;
int64_t nodes;
StateInfo* st; StateInfo* st;
// Static variables // Static variables
@@ -343,6 +336,9 @@ private:
static Key zobSideToMove; static Key zobSideToMove;
static Score PieceSquareTable[16][64]; static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
}; };
@@ -350,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];
} }
@@ -363,7 +367,7 @@ inline PieceType Position::type_of_piece_on(Square s) const {
} }
inline bool Position::square_is_empty(Square s) const { inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == EMPTY; return piece_on(s) == PIECE_NONE;
} }
inline bool Position::square_is_occupied(Square s) const { inline bool Position::square_is_occupied(Square s) const {
@@ -371,11 +375,11 @@ inline bool Position::square_is_occupied(Square s) const {
} }
inline Value Position::midgame_value_of_piece_on(Square s) const { inline Value Position::midgame_value_of_piece_on(Square s) const {
return piece_value_midgame(piece_on(s)); return PieceValueMidgame[piece_on(s)];
} }
inline Value Position::endgame_value_of_piece_on(Square s) const { inline Value Position::endgame_value_of_piece_on(Square s) const {
return piece_value_endgame(piece_on(s)); return PieceValueEndgame[piece_on(s)];
} }
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
@@ -387,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 {
@@ -507,11 +511,11 @@ inline Key Position::get_material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::pst(Color c, PieceType pt, Square s) const { inline Score Position::pst(Color c, PieceType pt, Square s) {
return PieceSquareTable[piece_of_color_and_type(c, pt)][s]; return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
} }
inline Score Position::pst_delta(Piece piece, Square from, Square to) const { inline Score Position::pst_delta(Piece piece, Square from, Square to) {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from]; return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
} }
@@ -531,22 +535,28 @@ 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 {
return startPosPlyCounter;
}
inline bool Position::opposite_colored_bishops() const { inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 return piece_count(WHITE, BISHOP) == 1
&& piece_count(BLACK, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& square_color(piece_list(WHITE, BISHOP, 0)) != square_color(piece_list(BLACK, BISHOP, 0)); && !same_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
} }
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 {
return isChess960;
}
inline bool Position::move_is_capture(Move m) const { inline bool Position::move_is_capture(Move m) const {
// Move must not be MOVE_NONE ! // Move must not be MOVE_NONE !
@@ -559,12 +569,20 @@ inline bool Position::move_is_capture_or_promotion(Move m) const {
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline PieceType Position::captured_piece() const { inline PieceType Position::captured_piece_type() const {
return st->capture; return st->capturedType;
} }
inline int Position::thread() const { 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)
+15 -13
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
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)
+103 -100
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);
@@ -68,28 +62,27 @@ const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok()); assert(pos.is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
Square from, to; string san;
PieceType pt; Square from = move_from(m);
Square to = move_to(m);
from = move_from(m); PieceType pt = type_of_piece(pos.piece_on(from));
to = move_to(m);
pt = type_of_piece(pos.piece_on(move_from(m)));
string san = "";
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:
break; break;
@@ -106,22 +99,25 @@ 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));
} }
} }
// Is the move check? We don't use pos.move_is_check(m) here, because
// Position::move_is_check doesn't detect all checks (not castling moves, // The move gives check ? We don't use pos.move_is_check() here
// promotions and en passant captures). // because we need to test for mate after the move is done.
StateInfo st; StateInfo st;
pos.do_move(m, st); pos.do_move(m, st);
if (pos.is_check()) if (pos.is_check())
@@ -141,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, OnePly, 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 = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
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')
@@ -195,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;
@@ -235,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
@@ -251,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
@@ -266,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
&& move_promotion_piece(m) == promotion
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
{ {
move = m; from = move_from(cur->move);
if ( pos.type_of_piece_on(from) == pt
&& 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 = cur->move;
matches++; matches++;
} }
return (matches == 1 ? move : MOVE_NONE); }
return matches == 1 ? move : MOVE_NONE;
} }
@@ -301,21 +304,21 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
size_t maxLength = 80 - startColumn; size_t maxLength = 80 - startColumn;
Position p(pos, pos.thread()); Position p(pos, pos.thread());
for (int i = 0; line[i] != MOVE_NONE; i++) for (Move* m = line; *m != MOVE_NONE; m++)
{ {
moveStr = move_to_san(p, line[i]); moveStr = move_to_san(p, *m);
length += moveStr.length() + 1; length += moveStr.length() + 1;
if (breakLines && length > maxLength) if (breakLines && length > maxLength)
{ {
s << '\n' << std::setw(startColumn) << ' '; s << "\n" << std::setw(startColumn) << " ";
length = moveStr.length() + 1; length = moveStr.length() + 1;
} }
s << moveStr << ' '; s << moveStr << ' ';
if (line[i] == MOVE_NULL) if (*m == MOVE_NULL)
p.do_null_move(st); p.do_null_move(st);
else else
p.do_move(line[i], st); p.do_move(*m, st);
} }
return s.str(); return s.str();
} }
@@ -326,26 +329,30 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
/// 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, const string pretty_pv(const Position& pos, int time, int depth,
uint64_t nodes, Value score, ValueType type, Move pv[]) { Value score, ValueType type, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
std::stringstream s; std::stringstream s;
// Depth // Depth
s << std::setw(2) << depth << " "; s << std::setw(2) << depth << " ";
// Score // Score
s << ((type == VALUE_TYPE_LOWER)? ">" : ((type == VALUE_TYPE_UPPER)? "<" : " ")); s << (type == VALUE_TYPE_LOWER ? ">" : type == VALUE_TYPE_UPPER ? "<" : " ")
s << std::setw(7) << score_string(score); << std::setw(7) << score_string(score);
// Time // Time
s << std::setw(8) << time_string(time) << " "; s << std::setw(8) << time_string(time) << " ";
// Nodes // Nodes
if (nodes < 1000000ULL) if (pos.nodes_searched() < M)
s << std::setw(8) << nodes << " "; s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (nodes < 1000000000ULL) else if (pos.nodes_searched() < K * M)
s << std::setw(7) << nodes/1000ULL << 'k' << " "; s << std::setw(7) << pos.nodes_searched() / K << "K ";
else else
s << std::setw(7) << nodes/1000000ULL << '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);
@@ -358,54 +365,50 @@ 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, OnePly, 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;
} }
const string time_string(int milliseconds) { const string time_string(int millisecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
std::stringstream s; std::stringstream s;
s << std::setfill('0'); s << std::setfill('0');
int hours = milliseconds / (1000*60*60); int hours = millisecs / MSecHour;
int minutes = (milliseconds - hours*1000*60*60) / (1000*60); int minutes = (millisecs - hours * MSecHour) / MSecMinute;
int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000; int seconds = (millisecs - hours * MSecHour - minutes * MSecMinute) / 1000;
if (hours) if (hours)
s << hours << ':'; s << hours << ':';
@@ -421,7 +424,7 @@ namespace {
if (v >= VALUE_MATE - 200) if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2; s << "#" << (VALUE_MATE - v + 1) / 2;
else if(v <= -VALUE_MATE + 200) else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2; s << "-#" << (VALUE_MATE + v) / 2;
else else
{ {
+1 -1
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)
-52
View File
@@ -1,52 +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(SCALE_H_INCLUDED)
#define SCALE_H_INCLUDED
////
//// Includes
////
#include "value.h"
////
//// Types
////
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
////
//// Inline functions
////
inline Value apply_scale_factor(Value v, ScaleFactor f) {
return Value((v * f) / int(SCALE_FACTOR_NORMAL));
}
#endif // !defined(SCALE_H_INCLUDED)
+1011 -1167
View File
File diff suppressed because it is too large Load Diff
+9 -14
View File
@@ -35,8 +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;
const int KILLER_MAX = 2;
//// ////
@@ -48,20 +47,19 @@ const int KILLER_MAX = 2;
/// 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 pv[PLY_MAX_PLUS_2];
Move currentMove; Move currentMove;
Move mateKiller; Move mateKiller;
Move threatMove;
Move excludedMove; Move excludedMove;
Move killers[KILLER_MAX]; Move bestMove;
Move killers[2];
Depth reduction; Depth reduction;
Value eval; Value eval;
Value evalMargin;
bool skipNullMove; bool skipNullMove;
SplitPoint* sp;
void init();
void initKillers();
}; };
@@ -72,11 +70,8 @@ struct SearchStack {
extern void init_search(); extern void init_search();
extern void init_threads(); extern void init_threads();
extern void exit_threads(); extern void exit_threads();
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move, extern int perft(Position& pos, Depth depth);
int time[], int increment[], int movesToGo, int maxDepth, extern bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[],
int maxNodes, int maxTime, Move searchMoves[]); int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]);
extern int perft(Position &pos, Depth depth);
extern int64_t nodes_searched();
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)
+29 -47
View File
@@ -57,55 +57,38 @@ 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(File);
ENABLE_OPERATORS_ON(Rank);
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
//// ////
inline File operator+ (File x, int i) { return File(int(x) + i); } inline Square operator+ (Square x, SquareDelta i) { return x + Square(i); }
inline File operator+ (File x, File y) { return x + int(y); } inline void operator+= (Square& x, SquareDelta i) { x = x + Square(i); }
inline void operator++ (File &x, int) { x = File(int(x) + 1); } inline Square operator- (Square x, SquareDelta i) { return x - Square(i); }
inline void operator+= (File &x, int i) { x = File(int(x) + i); } inline void operator-= (Square& x, SquareDelta i) { x = x - Square(i); }
inline File operator- (File x, int i) { return File(int(x) - i); }
inline void operator-- (File &x, int) { x = File(int(x) - 1); }
inline void operator-= (File &x, int i) { x = File(int(x) - i); }
inline Rank operator+ (Rank x, int i) { return Rank(int(x) + i); }
inline Rank operator+ (Rank x, Rank y) { return x + int(y); }
inline void operator++ (Rank &x, int) { x = Rank(int(x) + 1); }
inline void operator+= (Rank &x, int i) { x = Rank(int(x) + i); }
inline Rank operator- (Rank x, int i) { return Rank(int(x) - i); }
inline void operator-- (Rank &x, int) { x = Rank(int(x) - 1); }
inline void operator-= (Rank &x, int i) { x = Rank(int(x) - i); }
inline Square operator+ (Square x, int i) { return Square(int(x) + i); }
inline void operator++ (Square &x, int) { x = Square(int(x) + 1); }
inline void operator+= (Square &x, int i) { x = Square(int(x) + i); }
inline Square operator- (Square x, int i) { return Square(int(x) - i); }
inline void operator-- (Square &x, int) { x = Square(int(x) - 1); }
inline void operator-= (Square &x, int i) { x = Square(int(x) - i); }
inline Square operator+ (Square x, SquareDelta i) { return Square(int(x) + i); }
inline void operator+= (Square &x, SquareDelta i) { x = Square(int(x) + i); }
inline Square operator- (Square x, SquareDelta i) { return Square(int(x) - i); }
inline void operator-= (Square &x, SquareDelta i) { x = Square(int(x) - i); }
inline SquareDelta operator- (Square x, Square y) {
return SquareDelta(int(x) - int(y));
}
inline Square make_square(File f, Rank r) { inline Square make_square(File f, Rank r) {
return Square(int(f) | (int(r) << 3)); return Square(int(f) | (int(r) << 3));
@@ -135,8 +118,13 @@ inline Rank relative_rank(Color c, Square s) {
return square_rank(relative_square(c, s)); return square_rank(relative_square(c, s));
} }
inline Color square_color(Square s) { inline SquareColor square_color(Square s) {
return Color((int(square_file(s)) + int(square_rank(s))) & 1); return SquareColor((int(square_file(s)) + int(square_rank(s))) & 1);
}
inline bool same_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return (((s >> 3) ^ s) & 1) == 0;
} }
inline int file_distance(File f1, File f2) { inline int file_distance(File f1, File f2) {
@@ -175,15 +163,9 @@ 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) {
std::string str; return std::string(1, file_to_char(square_file(s)))
str += file_to_char(square_file(s)); + std::string(1, rank_to_char(square_rank(s)));
str += rank_to_char(square_rank(s));
return str;
} }
inline bool file_is_ok(File f) { inline bool file_is_ok(File f) {
+10 -9
View File
@@ -38,8 +38,8 @@
//// Constants and variables //// Constants and variables
//// ////
const int MAX_THREADS = 8; const int MAX_THREADS = 16;
const int ACTIVE_SPLIT_POINTS_MAX = 8; const int MAX_ACTIVE_SPLIT_POINTS = 8;
//// ////
@@ -55,6 +55,8 @@ struct SplitPoint {
bool pvNode, mateThreat; bool pvNode, mateThreat;
Value beta; Value beta;
int ply; int ply;
int master;
Move threatMove;
SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2]; SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2];
// Const pointers to shared data // Const pointers to shared data
@@ -63,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];
}; };
@@ -74,21 +77,19 @@ 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 {
volatile ThreadState state;
SplitPoint* volatile splitPoint; SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;
uint64_t nodes; SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
uint64_t betaCutOffs[2];
volatile ThreadState state;
unsigned char pad[64]; // set some distance among local data for each thread
}; };
+170
View File
@@ -0,0 +1,170 @@
/*
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 <cmath>
#include "misc.h"
#include "timeman.h"
#include "ucioption.h"
////
//// Local definitions
////
namespace {
/// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
// MoveImportance[] is based on naive statistical analysis of "how many games are still undecided
// after n half-moves". Game is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from CCRL game database with some simple filtering criteria.
const int MoveImportance[512] = {
7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780,
7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751,
7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425,
7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651,
6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462,
5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155,
4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011,
2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192,
2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605,
1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180,
1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888,
878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641,
634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460,
450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333,
328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240,
235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170,
170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118,
118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80,
80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54,
52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33,
31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20,
19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11,
9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[Min(ply, 511)]; }
/// Function Prototypes
enum TimeType { OptimumTime, MaxTime };
template<TimeType>
int remaining(int myTime, int movesToGo, int currentPly);
}
////
//// Functions
////
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(int myTime, int myInc, int movesToGo, int currentPly)
{
/* We support four different kind of time controls:
Inc == 0 && movesToGo == 0 means: x basetime [sudden death!]
Inc == 0 && movesToGo != 0 means: (x moves) / (y minutes)
Inc > 0 && movesToGo == 0 means: x basetime + z inc.
Inc > 0 && movesToGo != 0 means: (x moves) / (y minutes) + z inc
Time management is adjusted by following UCI parameters:
emergencyMoveHorizon :Be prepared to always play at least this many moves
emergencyBaseTime :Always attempt to keep at least this much time (in ms) at clock
emergencyMoveTime :Plus attempt to keep at least this much time for each remaining emergency move
minThinkingTime :No matter what, use at least this much thinking before doing the move
*/
int hypMTG, hypMyTime, t1, t2;
// Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>();
int emergencyBaseTime = Options["Emergency Base Time"].value<int>();
int emergencyMoveTime = Options["Emergency Move Time"].value<int>();
int minThinkingTime = Options["Minimum Thinking Time"].value<int>();
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = myTime;
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (movesToGo ? Min(movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = Max(myTime + (hypMTG - 1) * myInc - emergencyBaseTime - Min(hypMTG, emergencyMoveHorizon) * emergencyMoveTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2);
}
if (Options["Ponder"].value<bool>())
optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime);
}
////
//// Local functions
////
namespace {
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly)
{
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly);
int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++)
otherMovesImportance += move_importance(currentPly + 2 * i);
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
return int(floor(myTime * Min(ratio1, ratio2)));
}
}
+15 -13
View File
@@ -18,23 +18,25 @@
*/ */
#if !defined(MERSENNE_H_INCLUDED) #if !defined(TIMEMAN_H_INCLUDED)
#define MERSENNE_H_INCLUDED #define TIMEMAN_H_INCLUDED
////
//// Includes
////
#include "types.h"
//// ////
//// Prototypes //// Prototypes
//// ////
extern uint32_t genrand_int32(); class TimeManager {
extern uint64_t genrand_int64(); public:
extern void init_mersenne();
void init(int myTime, int myInc, int movesToGo, int currentPly);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
#endif // !defined(MERSENNE_H_INCLUDED) private:
int optimumSearchTime;
int maximumSearchTime;
int unstablePVExtraTime;
};
#endif // !defined(TIMEMAN_H_INCLUDED)
+22 -85
View File
@@ -23,10 +23,8 @@
//// ////
#include <cassert> #include <cassert>
#include <cmath>
#include <cstring> #include <cstring>
#include "movegen.h"
#include "tt.h" #include "tt.h"
// The main transposition table // The main transposition table
@@ -38,7 +36,7 @@ TranspositionTable TT;
TranspositionTable::TranspositionTable() { TranspositionTable::TranspositionTable() {
size = writes = 0; size = 0;
entries = 0; entries = 0;
generation = 0; generation = 0;
} }
@@ -56,8 +54,10 @@ void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1024; size_t newSize = 1024;
// We store a cluster of ClusterSize number of TTEntry for each position // Transposition table consists of clusters and
// and newSize is the maximum number of storable positions. // each cluster consists of ClusterSize number of TTEntries.
// Each non-empty entry contains information of exactly one position.
// newSize is the number of clusters we are going to allocate.
while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20)) while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2; newSize *= 2;
@@ -70,7 +70,7 @@ 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(); clear();
} }
@@ -80,7 +80,7 @@ void TranspositionTable::set_size(size_t mbSize) {
/// TranspositionTable::clear overwrites the entire transposition table /// TranspositionTable::clear overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the /// with zeroes. It is called whenever the table is resized, or when the
/// user asks the program to clear the table (from the UCI interface). /// user asks the program to clear the table (from the UCI interface).
/// Perhaps we should also clear it when the "ucinewgame" command is recieved? /// Perhaps we should also clear it when the "ucinewgame" command is received?
void TranspositionTable::clear() { void TranspositionTable::clear() {
@@ -88,18 +88,19 @@ void TranspositionTable::clear() {
} }
/// TranspositionTable::store writes a new entry containing a position, /// TranspositionTable::store writes a new entry containing position key and
/// a value, a value type, a search depth, and a best move to the /// valuable information of current position.
/// transposition table. Transposition table is organized in clusters of /// The Lowest order bits of position key are used to decide on which cluster
/// four TTEntry objects, and when a new entry is written, it replaces /// the position will be placed.
/// the least valuable of the four entries in a cluster. A TTEntry t1 is /// When a new entry is written and there are no empty entries available in cluster,
/// considered to be more valuable than a TTEntry t2 if t1 is from the /// it replaces the least valuable of entries.
/// A TTEntry t1 is considered to be more valuable than a TTEntry t2 if t1 is from the
/// current search and t2 is from a previous search, or if the depth of t1 /// current search and t2 is from a previous search, or if the depth of t1
/// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL /// is bigger than the depth of t2.
/// never replaces another entry for the same position.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key
@@ -108,25 +109,25 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
{ {
if (!tte->key() || tte->key() == posKey32) // empty or overwrite old if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
{ {
// Preserve any exsisting ttMove // Preserve any existing ttMove
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = tte->move(); m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD); tte->save(posKey32, v, t, d, m, generation, statV, kingD);
return; return;
} }
else if (i == 0) // replace would be a no-op in this common case
if (i == 0) // Replacing first entry is default and already set before entering for-loop
continue; continue;
int c1 = (replace->generation() == generation ? 2 : 0); c1 = (replace->generation() == generation ? 2 : 0);
int c2 = (tte->generation() == generation ? -2 : 0); c2 = (tte->generation() == generation ? -2 : 0);
int c3 = (tte->depth() < replace->depth() ? 1 : 0); c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(posKey32, v, t, d, m, generation, statV, kingD); replace->save(posKey32, v, t, d, m, generation, statV, kingD);
writes++;
} }
@@ -153,69 +154,5 @@ TTEntry* TranspositionTable::retrieve(const Key posKey) const {
/// entries from the current search. /// entries from the current search.
void TranspositionTable::new_search() { void TranspositionTable::new_search() {
generation++; generation++;
writes = 0;
}
/// TranspositionTable::insert_pv() is called at the end of a search
/// iteration, and inserts the PV back into the PV. This makes sure
/// the old PV moves are searched first, even if the old TT entries
/// have been overwritten.
void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
StateInfo st;
Position p(pos, pos.thread());
for (int i = 0; pv[i] != MOVE_NONE; i++)
{
TTEntry *tte = retrieve(p.get_key());
if (!tte || tte->move() != pv[i])
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i], VALUE_NONE, VALUE_NONE);
p.do_move(pv[i], st);
}
}
/// TranspositionTable::extract_pv() extends a PV by adding moves from the
/// transposition table at the end. This should ensure that the PV is almost
/// always at least two plies long, which is important, because otherwise we
/// will often get single-move PVs when the search stops while failing high,
/// and a single-move PV means that we don't have a ponder move.
void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PLY_MAX) {
const TTEntry* tte;
StateInfo st;
Position p(pos, pos.thread());
int ply = 0;
// Update position to the end of current PV
while (pv[ply] != MOVE_NONE)
p.do_move(pv[ply++], st);
// Try to add moves from TT while possible
while ( (tte = retrieve(p.get_key())) != NULL
&& tte->move() != MOVE_NONE
&& move_is_legal(p, tte->move())
&& (!p.is_draw() || ply < 2)
&& ply < PLY_MAX)
{
pv[ply] = tte->move();
p.do_move(pv[ply++], st);
}
pv[ply] = MOVE_NONE;
}
/// TranspositionTable::full() returns the permill of all transposition table
/// entries which have received at least one write during the current search.
/// It is used to display the "info hashfull ..." information in UCI.
int TranspositionTable::full() const {
double N = double(size) * ClusterSize;
return int(1000 * (1 - exp(writes * log(1.0 - 1.0/N))));
} }
+33 -27
View File
@@ -26,7 +26,7 @@
//// ////
#include "depth.h" #include "depth.h"
#include "position.h" #include "move.h"
#include "value.h" #include "value.h"
@@ -36,18 +36,20 @@
/// The TTEntry class is the class of transposition table entries /// The TTEntry class is the class of transposition table entries
/// ///
/// A TTEntry needs 96 bits to be stored /// A TTEntry needs 128 bits to be stored
/// ///
/// bit 0-31: key /// bit 0-31: key
/// bit 32-63: data /// bit 32-63: data
/// bit 64-79: value /// bit 64-79: value
/// bit 80-95: depth /// bit 80-95: depth
/// bit 96-111: static value
/// bit 112-127: margin of static value
/// ///
/// the 32 bits of the data field are so defined /// the 32 bits of the data field are so defined
/// ///
/// bit 0-16: move /// bit 0-16: move
/// bit 17-19: not used /// bit 17-20: not used
/// bit 20-22: value type /// bit 21-22: value type
/// bit 23-31: generation /// bit 23-31: generation
class TTEntry { class TTEntry {
@@ -56,21 +58,22 @@ public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value kd) { void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value kd) {
key32 = k; key32 = k;
data = (m & 0x1FFFF) | (t << 20) | (g << 23); data = (m & 0x1FFFF) | (t << 21) | (g << 23);
value16 = int16_t(v); value16 = int16_t(v);
depth16 = int16_t(d); depth16 = int16_t(d);
staticValue = int16_t(statV); staticValue = int16_t(statV);
kingDanger = 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); }
Move move() const { return Move(data & 0x1FFFF); } Move move() const { return Move(data & 0x1FFFF); }
Value value() const { return Value(value16); } Value value() const { return Value(value16); }
ValueType type() const { return ValueType((data >> 20) & 7); } ValueType type() const { return ValueType((data >> 21) & 3); }
int generation() const { return data >> 23; } int generation() const { return data >> 23; }
Value static_value() const { return Value(staticValue); } Value static_value() const { return Value(staticValue); }
Value king_danger() const { return Value(kingDanger); } Value static_value_margin() const { return Value(staticValueMargin); }
private: private:
uint32_t key32; uint32_t key32;
@@ -78,17 +81,16 @@ private:
int16_t value16; int16_t value16;
int16_t depth16; int16_t depth16;
int16_t staticValue; int16_t staticValue;
int16_t kingDanger; int16_t staticValueMargin;
}; };
/// This is the number of TTEntry slots for each position /// This is the number of TTEntry slots for each cluster
const int ClusterSize = 4; const int ClusterSize = 4;
/// Each group of ClusterSize number of TTEntry form a TTCluster /// TTCluster consists of ClusterSize number of TTEntries.
/// that is indexed by a single position key. TTCluster size must /// Size of TTCluster must not be bigger than a cache line size.
/// be not bigger then a cache line size, in case it is less then /// In case it is less, it should be padded to guarantee always aligned accesses.
/// it should be padded to guarantee always aligned accesses.
struct TTCluster { struct TTCluster {
TTEntry data[ClusterSize]; TTEntry data[ClusterSize];
@@ -96,11 +98,14 @@ struct TTCluster {
/// The transposition table class. This is basically just a huge array /// The transposition table class. This is basically just a huge array
/// containing TTEntry objects, and a few methods for writing new entries /// containing TTCluster objects, and a few methods for writing new entries
/// and reading new ones. /// and reading new ones.
class TranspositionTable { class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
public: public:
TranspositionTable(); TranspositionTable();
~TranspositionTable(); ~TranspositionTable();
@@ -109,33 +114,34 @@ public:
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD); void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD);
TTEntry* retrieve(const Key posKey) const; TTEntry* retrieve(const Key posKey) const;
void new_search(); void new_search();
void insert_pv(const Position& pos, Move pv[]);
void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
int full() const;
TTEntry* first_entry(const Key posKey) const; TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
private: private:
// Be sure 'writes' is at least one cache line away
// from read only variables.
unsigned char pad_before[64 - sizeof(unsigned)];
unsigned writes; // heavy SMP read/write access here
unsigned char pad_after[64];
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;
/// TranspositionTable::first_entry returns a pointer to the first /// TranspositionTable::first_entry returns a pointer to the first
/// entry of a cluster given a position. The low 32 bits of the key /// entry of a cluster given a position. The lowest order bits of the key
/// are used to get the index in the table. /// are used to get the index of the cluster.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const { 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)
+65 -1
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
@@ -109,4 +113,64 @@ inline void __cpuid(int CPUInfo[4], int)
} }
#endif #endif
// Templetized operators used by enum types like Depth, Piece, Square and so on.
// We don't want to write the same inline for each different enum. Note that we
// pass by value to silence scaring warnings when using volatiles.
// Because these templates override common operators and are included in all the
// files, there is a possibility that the compiler silently performs some unwanted
// overrides. To avoid possible very nasty bugs the templates are disabled by default
// and must be enabled for each type on a case by case base. The enabling trick
// uses template specialization, namely we just declare following struct.
template<typename T> struct TempletizedOperator;
// Then to enable the enum type we use following macro that defines a specialization
// of TempletizedOperator for the given enum T. Here is defined typedef Not_Enabled.
// Name of typedef is chosen to produce somewhat informative compile error messages.
#define ENABLE_OPERATORS_ON(T) \
template<> struct TempletizedOperator<T> { typedef T Not_Enabled; }
// Finally we use macro OK(T) to check if type T is enabled. The macro simply
// tries to use Not_Enabled, if was not previously defined a compile error occurs.
// The check is done fully at compile time and there is zero overhead at runtime.
#define OK(T) typedef typename TempletizedOperator<T>::Not_Enabled Type
template<typename T>
inline T operator+ (const T d1, const T d2) { OK(T); return T(int(d1) + int(d2)); }
template<typename T>
inline T operator- (const T d1, const T d2) { OK(T); return T(int(d1) - int(d2)); }
template<typename T>
inline T operator* (int i, const T d) { OK(T); return T(i * int(d)); }
template<typename T>
inline T operator* (const T d, int i) { OK(T); return T(int(d) * i); }
template<typename T>
inline T operator/ (const T d, int i) { OK(T); return T(int(d) / i); }
template<typename T>
inline T operator- (const T d) { OK(T); return T(-int(d)); }
template<typename T>
inline T operator++ (T& d, int) { OK(T); d = T(int(d) + 1); return d; }
template<typename T>
inline T operator-- (T& d, int) { OK(T); d = T(int(d) - 1); return d; }
template<typename T>
inline void operator+= (T& d1, const T d2) { OK(T); d1 = d1 + d2; }
template<typename T>
inline void operator-= (T& d1, const T d2) { OK(T); d1 = d1 - d2; }
template<typename T>
inline void operator*= (T& d, int i) { OK(T); d = T(int(d) * i); }
template<typename T>
inline void operator/= (T& d, int i) { OK(T); d = T(int(d) / i); }
#undef OK
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)
+118 -139
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,86 +34,47 @@
#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 {
// UCIInputParser is a class for parsing UCI input. The class // FEN string for the initial position
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// UCIParser is a class for parsing UCI input. The class
// is actually a string stream built on a given input string. // 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);
RootPosition.from_fen(StartPosition);
string command;
do {
// Wait for a command from stdin
if (!getline(cin, command))
command = "quit";
} while (handle_command(command));
}
////
//// Local functions
////
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; string token;
if (!(uip >> token)) // operator>>() skips any whitespace if (!(up >> token)) // operator>>() skips any whitespace
return true; return true;
if (token == "quit") if (token == "quit")
return false; return false;
if (token == "go") if (token == "go")
return go(uip); return go(pos, up);
if (token == "uci") if (token == "uci")
{ {
@@ -124,130 +84,151 @@ namespace {
cout << "uciok" << endl; cout << "uciok" << endl;
} }
else if (token == "ucinewgame") else if (token == "ucinewgame")
{ pos.from_fen(StartPositionFEN);
push_button("New Game");
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
else if (token == "isready") else if (token == "isready")
cout << "readyok" << endl; 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. else if (token == "position")
// Perhaps they should be removed later in order to reduce the set_position(pos, up);
// size of the program binary.
else if (token == "setoption")
set_option(up);
// The remaining commands are for debugging purposes only
else if (token == "d") else if (token == "d")
RootPosition.print(); pos.print();
else if (token == "flip") else if (token == "flip")
{ {
Position p(RootPosition, RootPosition.thread()); Position p(pos, pos.thread());
RootPosition.flipped_copy(p); pos.flipped_copy(p);
} }
else if (token == "eval") else if (token == "eval")
{ {
EvalInfo ei; Value evalMargin;
cout << "Incremental mg: " << mg_value(RootPosition.value()) cout << "Incremental mg: " << mg_value(pos.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value()) << "\nIncremental eg: " << eg_value(pos.value())
<< "\nFull eval: " << evaluate(RootPosition, ei) << endl; << "\nFull eval: " << evaluate(pos, evalMargin) << endl;
} }
else if (token == "key") else if (token == "key")
cout << "key: " << hex << RootPosition.get_key() cout << "key: " << hex << pos.get_key()
<< "\nmaterial key: " << RootPosition.get_material_key() << "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << RootPosition.get_pawn_key() << endl; << "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "perft") else if (token == "perft")
perft(uip); perft(pos, up);
else else
cout << "Unknown command: " << command << endl; cout << "Unknown command: " << cmd << endl;
return true; return true;
} }
////
//// Local functions
////
namespace {
// 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(StartPosition); {
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") if (token != "moves")
uip >> token; return;
if (token == "moves") // Parse optional move list
{
Move move; Move move;
StateInfo st; StateInfo st;
while (uip >> token) while (up >> token)
{ {
move = move_from_string(RootPosition, token); move = move_from_uci(pos, token);
RootPosition.do_move(move, st); pos.do_move(move, st);
if (RootPosition.rule_50_counter() == 0) if (pos.rule_50_counter() == 0)
RootPosition.reset_game_ply(); pos.reset_game_ply();
pos.inc_startpos_ply_counter(); //FIXME: make from_fen to support this and rule50
} }
// Our StateInfo st is about going out of scope so copy // Our StateInfo st is about going out of scope so copy
// its content inside RootPosition before they disappear. // its content inside pos before it disappears.
RootPosition.detach(); 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;
while (uip >> token && token != "value")
// Handle names with included spaces
while (up >> token && token != "value")
name += (" " + token); name += (" " + token);
if (token == "value" && uip >> value) if (Options.find(name) == Options.end())
{ {
while (uip >> token) cout << "No such option: " << name << endl;
return;
}
// 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); value += (" " + token);
set_option_value(name, value); Options[name].set_value(value);
} else
push_button(name);
}
} }
// 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
@@ -255,71 +236,69 @@ 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, RootPosition.side_to_move(), return think(pos, infinite, ponder, time, inc, movesToGo,
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();
n = perft(pos, depth * OnePly); n = perft(pos, depth * ONE_PLY);
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;
} }
} }
-31
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)
+99 -263
View File
@@ -17,173 +17,99 @@
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"
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
using std::cout;
using std::endl;
//// OptionsMap Options; // Global object
//// Local definitions
////
namespace {
/// // Our case insensitive less() function as required by UCI protocol
/// Types bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
///
enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON }; int c1, c2;
size_t i = 0;
typedef std::vector<string> ComboValues; while (i < s1.size() && i < s2.size())
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
struct Option { if (c1 != c2)
return c1 < c2;
string name, defaultValue, currentValue;
OptionType type;
size_t idx;
int minValue, maxValue;
ComboValues 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 this->idx < o.idx; }
};
typedef std::map<string, Option> Options;
///
/// Constants
///
// 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["Randomness"] = Option(0, 0, 10);
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["UCI_Chess960"] = Option(false);
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;
} }
return s1.size() < s2.size();
}
///
/// Variables
///
Options options; // stringify() converts a numeric value of type T to a std::string
template<typename T>
// stringify converts a value of type T to a std::string static string stringify(const T& v) {
template<typename T>
string stringify(const T& v) {
std::ostringstream ss; std::ostringstream ss;
ss << v; ss << v;
return ss.str(); return ss.str();
}
// get_option_value implements the various get_option_value_<type>
// functions defined later, because only the option value
// type changes a template seems a proper solution.
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;
}
} }
////
//// Functions
////
/// init_uci_options() initializes the UCI options. Currently, the only /// init_uci_options() initializes the UCI options to their hard coded default
/// thing this function does is to initialize the default value of the /// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// "Threads" parameter to the number of available CPU cores. /// 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);
// Set optimal value for parameter "Minimum Split Depth" // Set some SMP parameters accordingly to the detected CPU count
// according to number of available cores. Option& thr = Options["Threads"];
assert(options.find("Threads") != options.end()); Option& msd = Options["Minimum Split Depth"];
assert(options.find("Minimum Split Depth") != options.end());
Option& thr = options["Threads"];
Option& msd = options["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count()); thr.defaultValue = thr.currentValue = stringify(cpu_count());
@@ -193,150 +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() {
static const char optionTypeName[][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)
// Build up a vector out of the options map and sort it according to idx
// field, that is the chronological insertion order in options map.
std::vector<Option> vec;
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
vec.push_back(it->second);
std::sort(vec.begin(), vec.end());
for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{ {
std::cout << "\noption name " << it->name const Option& o = it->second;
<< " type " << optionTypeName[it->type]; cout << "\noption name " << it->first << " type " << o.type;
if (it->type == BUTTON) if (o.type != "button")
continue; cout << " default " << o.defaultValue;
if (it->type == CHECK) if (o.type == "spin")
std::cout << " default " << (it->defaultValue == "1" ? "true" : "false"); cout << " min " << o.minValue << " max " << o.maxValue;
else
std::cout << " default " << it->defaultValue;
if (it->type == SPIN) break;
std::cout << " min " << it->minValue << " max " << it->maxValue;
else if (it->type == COMBO)
for (ComboValues::const_iterator itc = it->comboValues.begin();
itc != it->comboValues.end(); ++itc)
std::cout << " var " << *itc;
} }
std::cout << std::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")
&& !(value == "true" || value == "false"))
return;
/// get_option_value_string() returns the current value of a UCI parameter as if (type == "spin")
/// 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. Note that
/// the function does not check that the new value is legal for the given
/// parameter: This is assumed to be the responsibility of the GUI.
void set_option_value(const string& name, const string& value) {
// 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";
if (options.find(name) == options.end())
{ {
std::cout << "No such option: " << name << std::endl; int v = atoi(value.c_str());
if (v < minValue || v > maxValue)
return; return;
} }
// Normally it's up to the GUI to check for option's limits, currentValue = value;
// 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;
else if (opt.type == SPIN)
{
int val = atoi(v.c_str());
if (val < opt.minValue || val > opt.maxValue)
return;
}
opt.currentValue = v;
}
/// 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;
}
namespace {
// Define constructors of Option class.
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) {}
} }
+51 -15
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)
-96
View File
@@ -1,96 +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 <sstream>
#include <string>
#include "value.h"
////
//// Functions
////
/// value_to_tt() adjusts a mate score from "plies to mate from the root" to
/// "plies to mate from the current ply". Non-mate scores are unchanged.
/// The function is called before storing a value to the transposition table.
Value value_to_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v + ply;
else if(v <= value_mated_in(100))
return v - ply;
else
return v;
}
/// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
/// from the transposition table to a mate score corrected for the current
/// ply depth.
Value value_from_tt(Value v, int ply) {
if(v >= value_mate_in(100))
return v - ply;
else if(v <= value_mated_in(100))
return v + ply;
else
return v;
}
/// value_to_centipawns() converts a value from Stockfish's somewhat unusual
/// scale of pawn = 256 to the more conventional pawn = 100.
int value_to_centipawns(Value v) {
return (int(v) * 100) / int(PawnValueMidgame);
}
/// value_from_centipawns() converts a centipawn value to Stockfish's internal
/// evaluation scale. It's used when reading the values of UCI options
/// containing material values (e.g. futility pruning margins).
Value value_from_centipawns(int cp) {
return Value((cp * 256) / 100);
}
/// value_to_string() converts a value to a string suitable for use with the
/// UCI protocol.
const std::string value_to_string(Value v) {
std::stringstream s;
if(abs(v) < VALUE_MATE - 200)
s << "cp " << value_to_centipawns(v);
else {
s << "mate ";
if(v > 0)
s << (VALUE_MATE - v + 1) / 2;
else
s << -(VALUE_MATE + v) / 2;
}
return s.str();
}
+21 -115
View File
@@ -21,13 +21,6 @@
#if !defined(VALUE_H_INCLUDED) #if !defined(VALUE_H_INCLUDED)
#define VALUE_H_INCLUDED #define VALUE_H_INCLUDED
////
//// Includes
////
#include "piece.h"
//// ////
//// Types //// Types
//// ////
@@ -41,6 +34,7 @@ enum ValueType {
enum Value { enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0, VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000, VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000, VALUE_MATE = 30000,
@@ -49,6 +43,16 @@ enum Value {
VALUE_ENSURE_SIGNED = -1 VALUE_ENSURE_SIGNED = -1
}; };
ENABLE_OPERATORS_ON(Value);
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single /// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame /// integer (enum), first LSB 16 bits are used to store endgame
@@ -56,7 +60,14 @@ enum Value {
// Compiler is free to choose the enum type as long as can keep // Compiler is free to choose the enum type as long as can keep
// its data, so ensure Score to be an integer type. // its data, so ensure Score to be an integer type.
enum Score { ENSURE_32_BITS_SIZE_P = (1 << 16), ENSURE_32_BITS_SIZE_N = -(1 << 16)}; enum Score {
SCORE_ZERO = 0,
SCORE_ENSURE_32_BITS_SIZE_P = (1 << 16),
SCORE_ENSURE_32_BITS_SIZE_N = -(1 << 16)
};
ENABLE_OPERATORS_ON(Score);
// Extracting the _signed_ lower and upper 16 bits it not so trivial // Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is // because according to the standard a simple cast to short is
@@ -73,13 +84,6 @@ inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (in
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
inline Score operator-(Score s) { return Score(-int(s)); }
inline Score operator+(Score s1, Score s2) { return Score(int(s1) + int(s2)); }
inline Score operator-(Score s1, Score s2) { return Score(int(s1) - int(s2)); }
inline void operator+=(Score& s1, Score s2) { s1 = Score(int(s1) + int(s2)); }
inline void operator-=(Score& s1, Score s2) { s1 = Score(int(s1) - int(s2)); }
inline Score operator*(int i, Score s) { return Score(i * int(s)); }
// Division must be handled separately for each term // Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
@@ -88,118 +92,20 @@ inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_v
inline Score operator*(Score s1, Score s2); inline Score operator*(Score s1, Score s2);
////
//// Constants and variables
////
/// Piece values, middle game and endgame
/// Important: If the material values are changed, one must also
/// adjust the piece square tables, and the method game_phase() in the
/// Position class!
///
/// Values modified by Joona Kiiski
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
const Value PieceValueMidgame[17] = {
Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0),
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame,
Value(0), Value(0), Value(0)
};
const Value PieceValueEndgame[17] = {
Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0),
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
RookValueEndgame, QueenValueEndgame,
Value(0), Value(0), Value(0)
};
/// Bonus for having the side to move (modified by Joona Kiiski)
const Score TempoValue = make_score(48, 22);
//// ////
//// Inline functions //// Inline functions
//// ////
inline Value operator+ (Value v, int i) { return Value(int(v) + i); } inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator+ (Value v1, Value v2) { return Value(int(v1) + int(v2)); }
inline void operator+= (Value &v1, Value v2) {
v1 = Value(int(v1) + int(v2));
}
inline Value operator- (Value v, int i) { return Value(int(v) - i); } inline Value operator- (Value v, int i) { return Value(int(v) - i); }
inline Value operator- (Value v) { return Value(-int(v)); }
inline Value operator- (Value v1, Value v2) { return Value(int(v1) - int(v2)); }
inline void operator-= (Value &v1, Value v2) {
v1 = Value(int(v1) - int(v2));
}
inline Value operator* (Value v, int i) { return Value(int(v) * i); }
inline void operator*= (Value &v, int i) { v = Value(int(v) * i); }
inline Value operator* (int i, Value v) { return Value(int(v) * i); }
inline Value operator/ (Value v, int i) { return Value(int(v) / i); }
inline void operator/= (Value &v, int i) { v = Value(int(v) / i); }
inline Value value_mate_in(int ply) { inline Value value_mate_in(int ply) {
return Value(VALUE_MATE - Value(ply)); return VALUE_MATE - ply;
} }
inline Value value_mated_in(int ply) { inline Value value_mated_in(int ply) {
return Value(-VALUE_MATE + Value(ply)); return -VALUE_MATE + ply;
} }
inline bool is_upper_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_UPPER)) != 0;
}
inline bool is_lower_bound(ValueType vt) {
return (int(vt) & int(VALUE_TYPE_LOWER)) != 0;
}
inline Value piece_value_midgame(PieceType pt) {
return PieceValueMidgame[pt];
}
inline Value piece_value_endgame(PieceType pt) {
return PieceValueEndgame[pt];
}
inline Value piece_value_midgame(Piece p) {
return PieceValueMidgame[p];
}
inline Value piece_value_endgame(Piece p) {
return PieceValueEndgame[p];
}
////
//// Prototypes
////
extern Value value_to_tt(Value v, int ply);
extern Value value_from_tt(Value v, int ply);
extern int value_to_centipawns(Value v);
extern Value value_from_centipawns(int cp);
extern const std::string value_to_string(Value v);
#endif // !defined(VALUE_H_INCLUDED) #endif // !defined(VALUE_H_INCLUDED)