Compare commits

..

151 Commits

Author SHA1 Message Date
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
Marco Costalba
726df58131 Stockfish 1.8 beta 1
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-24 18:00:14 +01:00
Marco Costalba
d9a8dd0f7a Revert "Do IID also when we already have a ttMove"
Joona's testing reports very bad results at 5s, 30s and
even 1 minute TC, so revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-24 18:00:03 +01:00
Marco Costalba
6776f76d20 Call apply_weight() for both colors in one go
Due to rounding errors in apply_weight() where we divide
by 0x100 it is not possible to keep some functionality.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-24 17:53:01 +01:00
Marco Costalba
74db0f0f40 Tweak unstoppable pawns detection
A pawn is unstoppable also if enemy king can reach it
but path to queening is protected.

Original idea by Ralph Stoesser fixed by me.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-24 17:52:56 +01:00
Marco Costalba
a010d438a2 Merge branch 'master' of free2.projectlocker.com:sf 2010-06-21 20:50:20 +01:00
Tord Romstad
a4988fecee Moved a misplaced #endif in misc.cpp, which broke compilation in
Mac OS X.

No functional change.
2010-06-21 11:25:06 +02:00
Marco Costalba
dc5caff638 Test killer for legality earlier
Many killers moves, around 40%, are not legal, so
skip earlier in this case.

Some Movepicker c'tor cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-19 17:27:10 +01:00
Marco Costalba
4e7a898d7e Optimize for king moves in see_sign()
Because we only test legal moves, a king move
cannot have negative SEE.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-19 15:58:14 +01:00
Marco Costalba
47ee6d9fa4 Move prefetch() out of TT
This code is platform specific and has nothing to
do with TT class, so move to misc.cpp

This patch is a prerequisite to use extend prefetch use
also to other hash tables apart from Transposition Table.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-19 11:10:54 +01:00
Marco Costalba
221f41c2df Extend checks only if SEE is non-negative
Idea from Dr. Hyatt

After 10k games at 5"+0 on my QUAD
Mod vs Orig +2750 =4601 -2649 +4 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-15 07:02:09 +01:00
Marco Costalba
a8b9c11f56 Revert "Use ply counter in Position object"
Search ply and game ply are rwo different things !

Revert bogus commit.

No functional change on bench, but it changes in real games
when engine sends all the moves up to current one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-13 02:38:16 +01:00
Marco Costalba
e9eea87341 Set LSNTime to 100 ms
This is a timeout compatible with very short TC of 5 sec/game.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-12 10:29:50 +01:00
Marco Costalba
a128faf0b0 Remove a wrong FIXME
If we are there it means we already had that info stored in TT,
so we don't need to overwrite with the same content !

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-06 00:17:33 +01:00
Marco Costalba
ed2754227a Avoid a double copy when saving a TTEntry
In statement:

*tte = TTEntry(posKey32, v, t, d, m, generation, statV, kingD);

We first create a TTEntry, then we copy the temporary entry to
its final destination in *tte then we discard the TTEntry.

Instead of this assign the fields directly to the destination TTEntry.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-05 12:35:44 +01:00
Marco Costalba
287b46aa63 Avoid calling evaluate() while razoring
Micro optimization that gives a 0.5% speed improvment

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-05 09:42:16 +02:00
Marco Costalba
a04dcce628 Offset pv[] always from 0
We don't need to offset from current ply.

Also rewritten a bit update_pv()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-04 19:41:41 +01:00
Marco Costalba
452f0d1696 Big qsearch() cleanup
It is more clear and should be a bit faster too.

Reverted also previous optimization patch because
seems do not increase actual speed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-03 19:15:44 +01:00
Marco Costalba
9337c6da46 Extend intermediate LMR to root search
Almost no change, but it is in sync with what we do in search
and in any case the ELO difference is very small (because the
events when the intermediate research triggers are very rare),
too small to be measured, we just verify we don't have any
unexpected regressions.

After 802 games at 1+0 full QUAD
Mod vs Orig +114 =581 -107 +3 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-03 12:17:05 +01:00
Marco Costalba
5f3c660d5d Shortcut futility pruning in qsearch
If we have pruned one capture due to its final value
we can prune also following ones because captures are
MVV ordered.

Also avoid a compare when not in PV because in that
case is always false.

No functional change.
2010-06-03 12:10:57 +01:00
Marco Costalba
ab127028ed Do not pass threadID as argument of search() and evaluate()
Get it from the position instead.

A good semplification of function calling and a speedup too.

No functional change also with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 20:48:40 +01:00
Marco Costalba
2f6927ac08 Save threadID info in Position
This is the best place because when we split we do a
copy of the position and also threadID, once set in a
given position, never changes anymore.

Forbid use of Position's default and copy c'tor to avoid
nasty bugs in case a position is created without explicitly
setting the threadID.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 20:19:43 +01:00
Marco Costalba
f148a8f6cc Don't initialize excludedMove and skipNullMove at each node
Do it once at the beginning becuase they are always reset
after use in the calling place where are set.

No functional change also with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 19:41:45 +01:00
Marco Costalba
c51e12200a Use SearchStack to pass allowNullmove
Also renamed allowNullmove in skipNullMove to reverse
the logic so that the field is initialized to 0 (false)
instead of 1 (true).

No functional change also with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 13:49:48 +01:00
Marco Costalba
5804bef824 Use SearchStack to pass excludedMove
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 13:22:48 +01:00
Marco Costalba
2572055c87 Fix white space breakage
No functional change with faked split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 11:25:02 +01:00
Marco Costalba
c6ba14b7c9 Sync sp_search() with main search()
And fix qserahc() dispatch also there.

No functional change tested wit Faked Split.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-02 10:49:34 +01:00
Marco Costalba
5212995563 Retire bitScanReverse32()
Use log() instead because we are not in speed
critical paths.

Also a bit of renaming and code shuffle while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 21:58:54 +01:00
Marco Costalba
50e094ef8d Retire ok_to_do_nullmove()
Has been remained the same from ages also with the FIXME.

Retire for now and rearrange the conditions order for
maximum performance.

Also a small touch at null zugzwang verification.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 20:50:08 +01:00
Marco Costalba
7903495b0a Move invariant of singular ext. check out of loop
It is almost always false.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 15:47:28 +01:00
Marco Costalba
a3819188de Rename ok_to_prune() in connected_threat()
It is more up to the point. Also small speedup
due to checking for threat move before calling
the function. This saves more then 90% of function calls.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 15:12:31 +01:00
Marco Costalba
c0136fb728 Avoid double function dispatch
In 44% of cases we call search() just to call
qsearch() one moment later, avoid this double dispatch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 14:47:55 +01:00
Marco Costalba
9b17083912 Retire init_node()
Also don't poll in qsearch()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 12:34:49 +01:00
Marco Costalba
05c5f08372 Don't init SplitPointStack[i][j].parent
It is already set to zero because is allocated in
the global storage area.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-06-01 07:36:30 +01:00
Marco Costalba
6716337f40 Use ply counter in Position object
And avoid a redundant one passed as argument in
search calls.

Also renamed gamePly in ply to better clarify this
is used as search ply and is set to zero at the
beginning of the search.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 22:03:22 +01:00
Marco Costalba
ee8ccac622 Fix SearchStack and ply misalignment in RootMoveList
In RootMoveList c'tor we call qsearch() with
ply == 1 but SearchStack at 0.

We never noticed before because in qsearch we don't access
previous's ply SearchStack, otherwise we would have got
a nice crash ;-)

This bug is a fall down of previous patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 18:33:43 +01:00
Marco Costalba
e4e12ed595 Convert SearchStack ss[] to SearchStack*
Use a pointer to current SearchStack to avoid ss[ply]
address calculation.

Gives 1% speedup on Intel compiler

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 17:36:16 +01:00
Marco Costalba
d81def4fa9 Add function to get ply from position
It will be used by future patches.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 12:10:39 +01:00
Marco Costalba
9446dd6da3 Move gamePly among the StateInfo data
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 12:09:18 +01:00
Marco Costalba
b7bc0d4c57 Move promotion and ep under pawn handling
And remove from main do_move() flow. Just a small speedup
because we avoid two branches in the common case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 12:08:34 +01:00
Marco Costalba
c35c18a705 Thread::splitPoint is a volatile pointer
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 06:16:50 +01:00
Marco Costalba
2ea323aec6 Do IID also when we already have a ttMove
In case tte->depth() is far lower the current depth
and we are in a PV node.

Almost 45% of researches give a different ttMove !

After 999 games at 1+0
Mod vs Orig +174 =694 -131 +15 ELO !!!!!!!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-31 06:15:39 +01:00
Marco Costalba
ec0f0eba6b If LMR search fails high research at intemediate depth
Do not search immediately at full depth, but try a second
chance at lower depth. This is a feature that should scale
well because become important at high depths where we have
big reductions and also big savings in avoiding a costly
full depth search.

After 942 games at 1+0
Mod vs Orig +158 =645 -139  +7 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-29 23:35:12 +01:00
Marco Costalba
0719470e50 Fix IIDMargin description
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-29 11:19:53 +01:00
Marco Costalba
3ccdb57d6f Retire zobMaterial[]
Use zobrist[] array to compute material key.

Space save of 2KB in L1 cache.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-29 08:54:15 +01:00
Marco Costalba
03cfd94414 Change zobMaterial[] index 0 definition
The index at 0 was reserved for no-pieces
information. But we don't need that.

This is a prerequisite for next patch.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-29 08:49:16 +01:00
Marco Costalba
8c32878701 Use Key type instead of Bitboard
They are both 64 bits unsigned integer, but it
is correct to use the proper type.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-29 08:11:36 +01:00
Marco Costalba
0c5e89e3e1 Revert non-linear threats evaluation
After 999 games at 1+0
Mod vs Orig +148 =712 -139 +3 ELO

The added complexity doesn't seems to pay off and could
even scale worst with longer TC. So revert.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-23 14:53:04 +01:00
Marco Costalba
4a081280ed Remove an useless assert in evaluate_passed_pawns()
The tested square comes from a bitboard anded with
pos.pieces_of_color(Us), so assert is useless.

Another nitpick report by Marek Kwiatkowski  ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 19:14:19 +01:00
Marco Costalba
ee9e162dd5 Account for double pawn push in evaluate_unstoppable_pawns()
One of the most nitpicking patches I have ever seen.

Of course almost no functional change, but added just
becasue we are very pedantic ;-)

Spotted by Marek Kwiatkowski

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 18:42:00 +01:00
Marco Costalba
7e82f793b8 Fix StormOpenFileBonus[] bug
It was erroneusly reversed. Bug from Glaurung times.
Probably a full re-tuning is needed anyhow.

Spotted and fixed by Ralph Stoesser.

After 999 games almost no change, but modified anyway
for documentation reasons.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 18:35:00 +01:00
Marco Costalba
a7bfaede91 Fix a warning on array of size 0 under Windows
And better document new reality.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 12:45:05 +01:00
Joona Kiiski
1afbe1a1d7 Drop completely illogical ei.kingDanger == 0 requirement
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 12:37:19 +01:00
Joona Kiiski
226bd54064 Always save static value and kingDanger to TT
Around 5% speed-up

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 12:37:11 +01:00
Joona Kiiski
de0c9e84ca Drop TTClusterSize from 5 to 4
Very small obvious functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 12:37:02 +01:00
Marco Costalba
66b751713e Add non-linear threats evaluation
Increase threats score according to the number of
threats and to the side to move.

Constants have been balanced after ~34k iterations.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 12:01:51 +01:00
Marco Costalba
ecc100d1bb Revert "Simple implementation of strong YBWC"
It does not seem to increase anything even with a QUAD,
so revert.

After 1000 games with a QUAD
Mod - Orig: 500 - 497 (+1 elo)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-22 11:36:51 +01:00
Joona Kiiski
85559cc597 Add some automatic detection for Windows
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-21 23:09:53 +01:00
Joona Kiiski
187451294f Documentation fix
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-21 06:56:57 +01:00
Joona Kiiski
ac4c971e06 Correct 'prefetch' handling for Makefile
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-21 06:56:48 +01:00
Marco Costalba
cab8b78846 Let prefetch to be enabled by default on Windows
When compiling with MSVC we don't use the Makefile
so tweak a bit the Makefile to allow to let prefetch
in by default so that it works under Windows.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 21:37:37 +01:00
Joona Kiiski
efdd1e697a Small tweaks to install gcc-profile-clean targets
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 21:23:00 +01:00
Joona Kiiski
09884756d8 Modify source to follow new Makefile
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 21:22:51 +01:00
Joona Kiiski
32590884df Rewrite Makefile
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 21:22:42 +01:00
Marco Costalba
977cd9520a Simple implementation of strong YBWC
No gain in the worst case of 2 threads, but also no
loss and good potential for QUAD or OCTAL machines.

After 922 games at 1+0 with 2 threads

Mod vs Orig +143 =533 -143 +0 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 21:17:47 +01:00
Marco Costalba
471e745a91 Remove an assert in evaluate_passed_pawns()
We already tested few lines before with:

assert(pos.pawn_is_passed(Us, s));

Spotted by Marek Kwiatkowski.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-20 17:48:55 +01:00
Marco Costalba
8dc4396477 Indentation fix in middle-game evaluation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-17 17:32:31 +01:00
Marco Costalba
6181e01c2a Introduce init_attack_tables() in evaluate()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-17 06:45:59 +01:00
Marco Costalba
0d207ec2c6 Do not consider discovered checks in king safety
Does not help and it slows downs a bit because it
is not cheap to get the possible discovered checks
out of a position.

After 997 games at 1+0
Mod vs orig +153 =692 -152  +0 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-16 15:37:42 +01:00
Marco Costalba
93f64577c3 Fix RootMove::operator<() description
Reported by Melvin Sprague.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-16 08:56:58 +01:00
Marco Costalba
8b6bcd9731 Remove an useless Max() in passed pawns evaluation
There is no reason for that since tr cannot become negative.

Spotted by Ralph Stoesser.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-16 00:56:05 +01:00
Marco Costalba
6c0b2f5003 Threath tuning results
Final values for threath tuning (after ~30k iterations)

Verified to be equivalent with tuning branch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-15 21:44:57 +01:00
Marco Costalba
52fd1a3d37 Add support for gcc-profile
It's now possible to build PGO builds with GCC

Patch from Oystein Johansen

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-15 07:51:46 +02:00
Marco Costalba
0c9c5032e8 Rename OutpostMask[] in AttackSpanMask[]
This is a more standard naming (see chessprogramming wiki)
and is more stick to what table is and not what is used for.

Code in pawns.cpp is a bit more readable now, at least for me ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-14 16:55:35 +01:00
Marco Costalba
9079bab84c Micro optimization in evaluate_pawns()
Avoid a double bitcount in test for candidate passed
pawn when we don't have any supporting pawn.

Also use outpost_mask() instead of build it up on
the fly.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-14 12:59:03 +01:00
Marco Costalba
ea5af9b8c0 Introduce evaluate_pawn_storm() to unify redundant code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-14 12:08:48 +01:00
Marco Costalba
0b49ec9822 Code style tweaks to pawns.cpp
Also a small speedup.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-14 12:05:17 +01:00
Marco Costalba
6c27bf1880 Change color argument of square_is_weak()
Pass the color for which the square is to be
considered weak, not the opposite.

It is more natural and intuitive in this way.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-14 11:59:25 +01:00
Marco Costalba
da1165ae5d Space inflate marsenne
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-13 21:53:56 +01:00
Marco Costalba
6e5bb3279f Another split() tweak session
Function split() doesn't need to return a value, also
remove useless 'master' field.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-13 21:53:55 +01:00
Joona Kiiski
e63ab4bd03 Document old test result
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-12 17:33:56 +01:00
Joona Kiiski
d72e862a5b Remove one hack caused by misunderstanding
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-12 17:33:42 +01:00
Marco Costalba
02fe05cd0d Fix a possible out of range access in previous patch
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-12 12:30:00 +01:00
Marco Costalba
16d6faf479 Retire splitPoint->cpus field
It is redundant with splitPoint->slaves[].

Also move slaves[MAX_THREADS] among the shared data in
SplitPoint definition as it should have been already.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-12 12:17:01 +01:00
Marco Costalba
2cec7347db Syncing sp_search() and search()
Small twekas to make the two searches as similar as
possible.

Also removed an useless setting of mateKiller in sp_search()

No functional change (tested with FakeSplit)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-12 07:01:50 +01:00
Marco Costalba
1288a5a10a Simplify init_safety()
In this for is also ready to be tuned....to be continued ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 12:03:32 +01:00
Marco Costalba
7f095b0a36 Greatly simplify weight_option()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 11:59:50 +01:00
Marco Costalba
2aa455ce95 Small evaluate_passed_pawns() cleanup
Mainly renamed local variables with sensible names.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 07:06:48 +01:00
Marco Costalba
c053f0b838 Better integration of faked split
We don't need to comment/uncomment code, just
set FakeSplit bool to true to enable faked split.

Also reintroduced some asserts and cleaned up a bit.

Tested that with FakeSplit = true we have reproducible
finger printing even in SMP.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 07:05:31 +01:00
Joona Kiiski
7abe5f12ef Disable fake-mode
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 07:05:30 +01:00
Joona Kiiski
253428bb3f Unite sp_search() and sp_search_pv()
Also introduce a new rule:
In sp_search() always must hold: sp->alpha < sp->beta
Should fix some rear but very nasty races

To keep everything in sync, search() is also modified to
obey this rule. Because this affects only PV-nodes, should
have zero meaning to speed.

No functional change in fake mode

Regression test after 854 games
Mod vs Orig 433 - 421, no crashes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-11 07:05:17 +01:00
Joona Kiiski
36f4fe52f0 Introduce fake-mode for split
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-09 23:06:13 +01:00
Marco Costalba
e4ad6a38ee Revert to old prefetch detection logic
It was broken on Windows 64bit with MSVC and possibly
on other platforms, so revert to old proven one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-09 13:05:21 +01:00
Marco Costalba
6c6b6cd1a4 Fix an off-by-one bug in ThreatBonus[] table
We need a retuning anyhow.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-09 11:30:50 +02:00
Joona Kiiski
c20a41c9cf Templatize qsearch
No functional change and 2% speed-up on GCC.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 23:32:31 +01:00
Marco Costalba
e0e4bdc991 Retire mate threat detection from evaluation
Remove a lot of complex, obscure and useless code.

After 999 games at 1+0
Mod vs Orig +162 =673 -164 -1 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 23:09:40 +01:00
Marco Costalba
35e39a196d Add a comment and a FIXME
And fix indentation too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 13:36:44 +01:00
Marco Costalba
276513c19f Lookup TT for eval also in PV nodes
We don't need to evaluate the position if it
is already cached in TT. We already do this
in non-PV case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 13:12:46 +01:00
Marco Costalba
9643d7524e Fix an obsoleted descrption comment
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 13:06:55 +01:00
Marco Costalba
b763b40101 Unify Internal iterative deepening
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 13:00:07 +01:00
Marco Costalba
b4870595a5 Templetize extension() function
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 12:41:36 +01:00
Marco Costalba
f010685136 Templetize reduction() functions
No functional or speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 12:26:34 +01:00
Marco Costalba
91ce930b28 Use enum NodeType instead of opaque true/false
Increase readibility.

No functional and speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 11:49:55 +01:00
Joona Kiiski
b075d8ca53 Unite search_pv() and search()
A lot of redundant code removed: -182 lines of code

No functional and speed change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-08 11:48:45 +01:00
Marco Costalba
c0334c7bac Rename AttackWeight[] in KingAttackWeights[]
Also simplify a bit the code removing useless
named constants.

No functional change.
2010-05-07 12:04:23 +01:00
Marco Costalba
0f712ad4fd Array ThreatBonus[] is initialized at zero by compiler
We don't need to do the job.

No functional change.
2010-05-07 12:04:23 +01:00
Marco Costalba
7488d216fd Properly indent evaluate_king()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-07 12:04:22 +01:00
Marco Costalba
b95b1a9705 Rename futilityMargin in kingDanger in EvalInfo
This is what actually is.

A standard naming convention suggests to name a variable
with someting resembling _what_ the variable is and not
_how_ the variable is used. This normally results
in easier to read code.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-07 12:04:22 +01:00
Marco Costalba
b14846b6d7 Simplify some obsolete code in king safety
Now that QueenCheckBonus and friends are always > 0
we can remove a bunch of useless 'if' statements.

No functional change.
2010-05-07 12:04:21 +01:00
Marco Costalba
921bd87280 Rename king "safety" to king "danger"
A bigger "safety" value is actually a bigger
threat for the king, so it is a bigger "danger"

With this renaming "Cowardice" and "Aggressiveness"
UCI parameters become easier to understand.

It is also easier to understand why the once "safety"
value (that is a "danger") is subtracted from evaluation
instead of being added.

No functional change.
2010-05-07 12:04:21 +01:00
Marco Costalba
569bc75eb8 Evaluation weights cleanup
Use a Weights[] array instead of named variables to
store evaluation weights.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-07 12:04:20 +01:00
Marco Costalba
0544d6c8f7 Set Mate Threat Extension to OnePly
For both PV and non-PV nodes.

After 981 games at 1+0
Mod vs Orig +153 =686 -142 +4 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-07 12:04:08 +01:00
Marco Costalba
fe76787a77 Avoid a call to apply_weight() in evaluate_king()
Precompute scores in SafetyTable[] instead of calculate
them on the fly.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 13:56:54 +02:00
Joona Kiiski
8e269d781a Further push the LMR pedal
More aggressive LMR reductions.

Tested at different time controls:

- Tested with 1CPU 1+0, after 3000 games, result was +12 ELO

- Tested this with 4CPU 1+0 and got sth around 5-10 ELO increase

- Last one at long time control,after ~1000 games with 10+0 result is:

Orig - Mod: 491 - 520 (+10 elo)

A testing marathon by Joona for this important change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 12:20:39 +01:00
Marco Costalba
df7cd94aee A promotion piece cannot be a king or a pawn
Or any other garbage value bigger then QUEEN.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 12:16:16 +01:00
Marco Costalba
f16c231bc9 Do not return from idle_loop() with lock held
Master thread returns from idle_loop() when sp->cpus == 0,
but cpus is decremented by slave threads under sp->lock,
so it could happen that we return in split(), where we release
the split point, with sp->lock still held.

This patch guarantees that sp->lock is released when returning
to split().

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 12:16:12 +01:00
Marco Costalba
b89733b46c Reverse the logic used to detect prefetch
Explicitly search for x86 architecture instead of
excluding the others.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 12:16:10 +01:00
Marco Costalba
67f611f3eb Allow a static evaluation to overwrite an exsisting entry
The idea here is that if we cut-off after a stand pat the
already exsisting TT entry was not usable with current
beta, so overwrite anyway.

After 999 games at 1+0
Mod vs Orig +173 =665 -161  + 4 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-05 12:15:40 +01:00
Marco Costalba
80f5ca88f6 Do not refresh TT in qsearch
Almost no change and simplifies a bit the code.

After 961 games at 1+0
Mod vs Orig +156 =650 -146  +4 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-02 07:50:19 +01:00
Marco Costalba
8425b2b499 Refresh TT entry after a cut-off to avoid aging
Re-save the same TT entry if value is usable and allow
us to cut-off, it means that entry is valuable and
we want to keep it fresh updating the 'generation'
parameter up to the current value.

Patch suggested by J. Wesley Cleveland and better
clarified by Miguel A. Ballicora.

After 999 games at 1+0 64MB hash size
Mod vs Orig +167 =677 -155 +4 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-05-01 05:09:55 +01:00
Joona Kiiski
a086f34f36 Fix compile error on GCC
Add missing prototype.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-28 12:01:00 +02:00
Marco Costalba
83631c89ce Endgame's apply() method can be 'const'
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-27 06:17:32 +01:00
Marco Costalba
bedf80a4c0 Remove an obsolete comment
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-27 06:09:04 +01:00
Marco Costalba
cb9399445f Another small material tweak
In this case we avoid to name the 'black' version of the
endgame function but use a vector indexed by color instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-26 18:45:28 +01:00
Marco Costalba
fe7e0a425e Cleanup material distribution detectors
No functional change (verified each function)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-26 12:21:22 +01:00
Marco Costalba
fb0e19dc8b Do not call exit_threads() in Application d'tor
Because exit_threads() references the global object TM, we
need to call the function when still inside main(), otherwise,
due to undefined global object initialization and destruction
we could end up with referencing an already destroyed object.

Actually this should not happen because Application singleton
is initialized _only_ after all the other globals due to how
Application::initialize() is defined, but this is very tricky
C++ and not easy to follow, even for me ;-)

Also rearranged a bit main() code flow.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-25 10:35:55 +01:00
Marco Costalba
e6b5d03cc4 Small passed pawns evaluation cleanup
Moved evaluation of unstoppable pawns out of
evauation of passed pawns because event frequency is
much lower. Added evaluate_unstoppable_pawns() that
is called very seldom and contains all the unstoppable
pawn logic.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-25 09:50:27 +01:00
Marco Costalba
b2c1e15698 Simplify a bit futility marging formula
Should be a very minor change, but there is a small
functional change because futility_margin() is used in more
places then in the pruning formula.

After 999 games at 1+0
Mod vs Orig +167 =678 -154  +5 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-23 18:52:22 +01:00
Marco Costalba
11207f7c1f Revert scale factor in pawn evaluation
It simply doesn't seems to work both in direct matches
and in balance tuning.

So revert the idea.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-21 20:35:47 +01:00
Marco Costalba
97f5d19bdc Introduce PawnsQtyTable[] to refine pawn's drawish calculation
Also fix dimension of UnpairedPawnsTable[] to accomodate the
case in which we have 8 unpaired pawns, i.e. only one side has
pawns, the other side has no pawns.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-19 12:24:26 +01:00
Marco Costalba
fc89dbcab2 First attempt at tweaking UnpairedPawnsTable[] values
Values by Joona.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-19 06:37:07 +01:00
Marco Costalba
6b7efa0cd1 Introduce scale factor in pawn evaluation
The idea is to reduce the score if we have many
pawns opposing an enemy pawn so that the draw
possibility increases.

Just introduced the logic, but no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-19 06:21:13 +01:00
Marco Costalba
c23cd4d90a Fix candidate passed pawn definition
A pawn is candidate to be passed if doesn't have enemy pawns
in just front of him, not also behind !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-18 10:09:12 +01:00
Marco Costalba
a9fa1fc7f7 Retire Position::pawn_is_passed() and friends
Absolutely no useful at all, just code obfuscation so
use real definition instead.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-18 09:58:57 +01:00
Marco Costalba
1f1ef0897c Introduce table SquaresInFrontMask[2][64]
It will be used to lookup squares in front of
a given square. Same concept of PassedPawnMask[]
and OutpostMask[].

Also small tweaks in bitboard.h

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-18 09:47:31 +01:00
Marco Costalba
a49e4fac98 Better perft integration in benchmark
Now with:

   stockfish bench 128 1 5 default perft

it is possible to get perft 5 results of each position and
the first 3 positions correspond to the well known test
position in:

http://chessprogramming.wikispaces.com/Perft+Results

This allow to quickly check for perft consistency running
the 'bench' command.

No functional change but signature has changed because
bench default positions 2 and 3 have changed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-18 09:18:49 +01:00
Marco Costalba
87379c2929 Space inflate bitboard.cpp
This file, somehow, avoided the "space inflate" treatment...until now ;-)

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-18 08:58:26 +01:00
Marco Costalba
53b522d95d Convert polyglot.ini to use Linux line ending
Instead of Windows cr/lf

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-17 11:24:25 +01:00
Marco Costalba
ef0bbe6e18 Teach polyglot the new "Best Book Move" UCI option
Also turn off log by default as is in UCI case.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-17 11:22:40 +01:00
Marco Costalba
65c2715d9a Revert saving of null search value in TT
Revert all the patches that introduced the change and
more or less fixed the zugzwang issue.

There is a gain against last current version and we
can remove a lot of code.

After 979 games at 1+0 on my QUAD
Mod vs Orig +152 =688 -139 +5 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-17 10:57:37 +01:00
Marco Costalba
ec0a650dff Don't overwrite exsisting TT with null search value
Real search is considered of higher quality then null
search one.

This allows to fix the zugzwang issue with a minimal
impact on ELO.

Zugzwang verified on position:

8/7P/8/8/K2b4/p7/1k6/1B6 b - -

After 999 games at 1+0 on my QUAD
Mod vs Orig(94bb196) +168 =657 -174  -2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-16 05:52:18 +01:00
Joona Kiiski
abae3c5678 Prevent the use of nullmove TT value only at verification search
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-16 05:47:31 +01:00
Joona Kiiski
f3809f2a18 Introduce NullStatus enum
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-16 05:47:29 +01:00
Joona Kiiski
81ae7cad2d Revert "Introduce "Zugzwang detection" temporary hack for 1.7.1"
This reverts commit f9d3b48ad0.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-16 05:47:28 +01:00
Marco Costalba
94bb1964f6 Add "Best Book Move" UCI option
Is a boolean option that when set allows Stockfish
to select the best book move across the possible ones.

Feature requested by Salvo Spitaleri.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-16 05:45:30 +01:00
Joona Kiiski
13431922a3 Fix overflow in init_safety
Also write the code in more clean way

No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-14 19:19:11 +01:00
Joona Kiiski
9a3fc4d3fb Fix evasion pruning condition
Avoid incorrect mate scores in positions like

BK5/1R4b1/2k1Np2/3p3b/2p3pq/p1rB4/n2n1p2/8 w - -

Thanks for Jouni Uski for reporting the problem

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-13 20:28:49 +01:00
Marco Costalba
a4a0ffce71 Fix some warnings under +w1 HP-UX compile
This is the world's fussiest compiler with +w1

Warnings reported by Richard Lloyd.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-11 17:03:03 +01:00
Marco Costalba
e81108a855 Restore development version
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-10 20:42:31 +01:00
Marco Costalba
f967f1a25e Update polyglot.ini
Remove obsolete options and add a few ones.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-04-10 20:41:10 +01:00
38 changed files with 2081 additions and 2289 deletions

View File

@@ -7,7 +7,7 @@ EngineCommand = ./stockfish
Book = false Book = false
BookFile = book.bin BookFile = book.bin
Log = true Log = false
LogFile = stockfish.log LogFile = stockfish.log
Resign = true Resign = true
@@ -19,28 +19,18 @@ Hash = 128
Threads = 1 Threads = 1
OwnBook = false OwnBook = false
Book File = book.bin Book File = book.bin
Best Book Move = false
Use Search Log = false Use Search Log = false
Search Log Filename = SearchLog.txt
Mobility (Middle Game) = 100 Mobility (Middle Game) = 100
Mobility (Endgame) = 100 Mobility (Endgame) = 100
Pawn Structure (Middle Game) = 100 Pawn Structure (Middle Game) = 100
Pawn Structure (Endgame) = 100 Pawn Structure (Endgame) = 100
Passed Pawns (Middle Game) = 100 Passed Pawns (Middle Game) = 100
Passed Pawns (Endgame) = 100 Passed Pawns (Endgame) = 100
Space = 100
Aggressiveness = 100 Aggressiveness = 100
Cowardice = 100 Cowardice = 100
King Safety Curve = Quadratic
Quadratic = Linear
King Safety Coefficient = 40
King Safety X Intercept = 0
King Safety Max Slope = 30
King Safety Max Value = 500
Queen Contact Check Bonus = 3
Queen Check Bonus = 2
Rook Check Bonus = 1
Bishop Check Bonus = 1
Knight Check Bonus = 1
Discovered Check Bonus = 3
Mate Threat Bonus = 3
Check Extension (PV nodes) = 2 Check Extension (PV nodes) = 2
Check Extension (non-PV nodes) = 1 Check Extension (non-PV nodes) = 1
Single Reply Extension (PV nodes) = 2 Single Reply Extension (PV nodes) = 2
@@ -53,11 +43,6 @@ Passed Pawn Extension (PV nodes) = 1
Passed Pawn Extension (non-PV nodes) = 0 Passed Pawn Extension (non-PV nodes) = 0
Pawn Endgame Extension (PV nodes) = 2 Pawn Endgame Extension (PV nodes) = 2
Pawn Endgame Extension (non-PV nodes) = 2 Pawn Endgame Extension (non-PV nodes) = 2
Full Depth Moves (PV nodes) = 14
Full Depth Moves (non-PV nodes) = 3
Threat Depth = 5
Futility Pruning (Main Search) = true
Futility Pruning (Quiescence Search) = true
Randomness = 0 Randomness = 0
Minimum Split Depth = 4 Minimum Split Depth = 4
Maximum Number of Threads per Split Point = 5 Maximum Number of Threads per Split Point = 5

View File

@@ -1,7 +1,7 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2007 Tord Romstad # Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008 Marco Costalba # Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
#
# This file is part of Stockfish. # This file is part of Stockfish.
# #
# Stockfish is free software: you can redistribute it and/or modify # Stockfish is free software: you can redistribute it and/or modify
@@ -18,293 +18,485 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
### Executable name. Do not change ### ==========================================================================
### Section 1. General Configuration
### ==========================================================================
### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions ### Installation dir definitions
PREFIX = /usr/local PREFIX = /usr/local
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
### ==========================================================================
### Compiler speed switches for both GCC and ICC. These settings are generally
### fast on a broad range of systems, but may be changed experimentally
### ==========================================================================
GCCFLAGS = -O3 -msse
ICCFLAGS = -fast -msse
ICCFLAGS-OSX = -fast -mdynamic-no-pic
### ==========================================================================
### Enable/disable debugging, disabled by default
### ==========================================================================
GCCFLAGS += -DNDEBUG
ICCFLAGS += -DNDEBUG
ICCFLAGS-OSX += -DNDEBUG
### ==========================================================================
### Remove below comments to compile for a big-endian machine
### ==========================================================================
#GCCFLAGS += -DBIGENDIAN
#ICCFLAGS += -DBIGENDIAN
#ICCFLAGS-OSX += -DBIGENDIAN
### ==========================================================================
### Run built-in benchmark for pgo-builds with: 32MB hash 1 thread 10 depth
### These settings are generally fast, but may be changed experimentally
### ==========================================================================
PGOBENCH = ./$(EXE) bench 32 1 10 default depth PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files
### General compiler settings. Do not change
GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti
ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
### General linker settings. Do not change
LDFLAGS = -lpthread
### Object files. Do not change
OBJS = application.o bitboard.o pawns.o material.o endgame.o evaluate.o main.o \ OBJS = application.o 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 piece.o \
position.o direction.o tt.o value.o uci.o ucioption.o \ position.o direction.o tt.o value.o uci.o ucioption.o \
mersenne.o book.o bitbase.o san.o benchmark.o mersenne.o book.o bitbase.o san.o benchmark.o
### General rules. Do not change ### ==========================================================================
### Section 2. High-level Configuration
### ==========================================================================
#
# flag --- Comp switch --- Description
# ----------------------------------------------------------------------------
#
# debug = no/yes --- -DNDEBUG --- Enable/Disable debug mode
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = no/yes --- -DBIGENDIAN --- big/little-endian byte order
# prefetch = no/yes --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = no/yes --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction
# --- (Works only with GCC and ICC 64-bit)
# popcnt = no/yes --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
# at the end of the line for flag values.
### 2.1. General
debug = no
optimize = yes
### 2.2 Architecture specific
# General-section
ifeq ($(ARCH),general-64)
arch = any
os = any
bits = 64
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),general-32)
arch = any
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-64)
arch = any
os = any
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-32)
arch = any
os = any
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
# x86-section
ifeq ($(ARCH),x86-64)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = yes
endif
ifeq ($(ARCH),x86-32)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
# osx-section
ifeq ($(ARCH),osx-ppc-64)
arch = ppc64
os = osx
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-ppc-32)
arch = ppc
os = osx
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-x86-64)
arch = x86_64
os = osx
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),osx-x86-32)
arch = i386
os = osx
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
### ==========================================================================
### Section 3. Low-level configuration
### ==========================================================================
### 3.1 Selecting compiler (default = gcc)
ifeq ($(COMP),)
COMP=gcc
endif
ifeq ($(COMP),gcc)
comp=gcc
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),icc)
comp=icc
CXX=icpc
profile_prepare = icc-profile-prepare
profile_make = icc-profile-make
profile_use = icc-profile-use
profile_clean = icc-profile-clean
endif
### 3.2 General compiler settings
CXXFLAGS += -g -Wall -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),icc)
CXXFLAGS += -wd383,869,981,10187,10188,11505,11503
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
endif
### 3.3 General linker settings
LDFLAGS += -lpthread $(EXTRALDFLAGS)
ifeq ($(os),osx)
LDFLAGS += -arch $(arch)
endif
### 3.4 Debugging
ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
endif
### 3.5 Optimization
ifeq ($(optimize),yes)
ifeq ($(comp),gcc)
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
ifeq ($(comp),icc)
CXXFLAGS += -fast
ifeq ($(os),osx)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
### 3.6. Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 Endianess
ifeq ($(bigendian),yes)
CXXFLAGS += -DBIGENDIAN
endif
### 3.8 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.9 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.10 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -DUSE_POPCNT
endif
### ==========================================================================
### Section 4. Public targets
### ==========================================================================
default: default:
$(MAKE) gcc $(MAKE) ARCH=$(ARCH) COMP=$(COMP) build
help: help:
@echo "" @echo ""
@echo "Makefile options:" @echo "To compile stockfish, type: "
@echo "" @echo ""
@echo "make > Default: Compiler = g++" @echo "make target ARCH=arch [COMP=comp]"
@echo "make gcc-popcnt > Compiler = g++ + popcnt-support"
@echo "make icc > Compiler = icpc"
@echo "make icc-profile > Compiler = icpc + automatic pgo-build"
@echo "make icc-profile-popcnt > Compiler = icpc + automatic pgo-build + popcnt-support"
@echo "make osx-ppc32 > PPC-Mac OS X 32 bit. Compiler = g++"
@echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
@echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
@echo "make osx-x86_64 > x86-Mac OS X 64 bit. Compiler = g++"
@echo "make osx-icc32 > x86-Mac OS X 32 bit. Compiler = icpc"
@echo "make osx-icc64 > x86-Mac OS X 64 bit. Compiler = icpc"
@echo "make osx-icc32-profile > OSX 32 bit. Compiler = icpc + automatic pgo-build"
@echo "make osx-icc64-profile > OSX 64 bit. Compiler = icpc + automatic pgo-build"
@echo "make hpux > HP-UX. Compiler = aCC"
@echo "make strip > Strip executable"
@echo "make clean > Clean up"
@echo "" @echo ""
@echo "Supported targets:"
@echo ""
@echo "build > Build unoptimized version"
@echo "profile-build > Build PGO-optimized version"
@echo "popcnt-profile-build > Build PGO-optimized version with optional popcnt-support"
@echo "strip > Strip executable"
@echo "install > Install executable"
@echo "clean > Clean up"
@echo "testrun > Make sample run"
@echo ""
@echo "Supported archs:"
@echo ""
@echo "x86-64 > x86 64-bit"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt-instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit"
@echo "osx-x86-32 > x86-Mac OS X 32 bit"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo "bigendian-64 > unspecified 64-bit with bigendian byte order"
@echo "bigendian-32 > unspecified 32-bit with bigendian byte order"
@echo ""
@echo "Supported comps:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo ""
@echo "Non-standard targets:"
@echo ""
@echo "make hpux > Compile for HP-UX. Compiler = aCC"
@echo ""
@echo "Examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make profile-build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make profile-build ARCH=x86-32 (This is for 32-bit systems)"
@echo ""
build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
@echo ""
@echo "Step 0/4. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/4. Building executable for benchmark ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/4. Building final executable ..."
@touch *.cpp
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
popcnt-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
@echo ""
@echo "Step 0/6. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/6. Building executable for benchmark (popcnt disabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 6/6. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
@echo ""
strip:
strip $(EXE)
install:
-mkdir -p -m 755 $(BINDIR)
-cp $(EXE) $(BINDIR)
-strip $(BINDIR)/$(EXE)
clean:
$(RM) $(EXE) *.o .depend *~ core bench.txt
testrun:
@$(PGOBENCH)
### ==========================================================================
### Section 5. Private targets
### ==========================================================================
all: $(EXE) .depend all: $(EXE) .depend
test check: default config-sanity:
@$(PGOBENCH) @echo ""
@echo "Config:"
@echo "debug: '$(debug)'"
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "os: '$(os)'"
@echo "bits: '$(bits)'"
@echo "bigendian: '$(bigendian)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@echo "CXXFLAGS: $(CXXFLAGS)"
@echo "LDFLAGS: $(LDFLAGS)"
@echo ""
@echo "Testing config sanity. If this fails, try 'make help' ..."
@echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc"
@test "$(os)" = "any" || test "$(os)" = "osx"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(bigendian)" = "yes" || test "$(bigendian)" = "no"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc"
clean: $(EXE): $(OBJS)
$(RM) *.o .depend *~ $(EXE) core bench.txt $(CXX) -o $@ $(OBJS) $(LDFLAGS)
gcc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean
### Possible targets. You may add your own ones here gcc-profile-make:
gcc: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
$(MAKE) \ EXTRACXXFLAGS='-fprofile-generate' \
CXX='g++' \ EXTRALDFLAGS='-lgcov' \
CXXFLAGS="$(GCCFLAGS)" \
all all
gcc-popcnt: gcc-profile-use:
$(MAKE) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
CXX='g++' \ EXTRACXXFLAGS='-fprofile-use' \
CXXFLAGS="$(GCCFLAGS) -DUSE_POPCNT" \
all all
gcc-profile-clean:
@rm -rf *.gcda *.gcno bench.txt
icc: icc-profile-prepare:
$(MAKE) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean
CXX='icpc' \ @mkdir profdir
CXXFLAGS="$(ICCFLAGS)" \
all
icc-profile-make: icc-profile-make:
$(MAKE) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
CXX='icpc' \ EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all all
icc-profile-use: icc-profile-use:
$(MAKE) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
CXX='icpc' \ EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all all
icc-profile: icc-profile-clean:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) icc-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) icc-profile-use
@rm -rf profdir bench.txt @rm -rf profdir bench.txt
icc-profile-make-with-popcnt: .depend:
$(MAKE) \ -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use-with-popcnt: -include .depend
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile-popcnt:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) icc-profile-make
@echo ""
@echo "Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null
@touch *.cpp *.h
$(MAKE) icc-profile-make-with-popcnt
@echo ""
@echo "Running benchmark for pgo-build (popcnt enabled)..."
@$(PGOBENCH) > /dev/null
@echo "Benchmarks finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) icc-profile-use-with-popcnt
@rm -rf profdir bench.txt
osx-ppc32: ### ==========================================================================
$(MAKE) \ ### Section 6. Non-standard targets
CXX='g++' \ ### ==========================================================================
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc' \
CXXFLAGS+='-DBIGENDIAN' \
LDFLAGS+='-arch ppc' \
all
osx-ppc64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc64' \
CXXFLAGS+='-DBIGENDIAN' \
LDFLAGS+='-arch ppc64' \
all
osx-x86:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch i386 -mdynamic-no-pic' \
LDFLAGS+='-arch i386 -mdynamic-no-pic' \
all
osx-x86_64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch x86_64 -mdynamic-no-pic' \
LDFLAGS+='-arch x86_64 -mdynamic-no-pic' \
all
osx-icc32:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
LDFLAGS+='-arch i386' \
all
osx-icc64:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
LDFLAGS+='-arch x86_64' \
all
osx-icc32-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-use
@rm -rf profdir bench.txt
osx-icc64-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-use
@rm -rf profdir bench.txt
hpux: hpux:
$(MAKE) \ $(MAKE) \
@@ -313,23 +505,3 @@ hpux:
LDFLAGS="" \ LDFLAGS="" \
all all
strip:
strip $(EXE)
### Compilation. Do not change
$(EXE): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
### Installation
install: default
-mkdir -p -m 755 $(BINDIR)
-cp $(EXE) $(BINDIR)
-strip $(BINDIR)/$(EXE)
### Dependencies. Do not change
.depend:
-@$(CXX) -msse -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null
-include .depend

View File

@@ -57,12 +57,6 @@ Application::Application() {
genrand_int32(); genrand_int32();
} }
Application::~Application() {
exit_threads();
quit_eval();
}
void Application::initialize() { void Application::initialize() {
// A static Application object is allocated // A static Application object is allocated
@@ -70,7 +64,15 @@ void Application::initialize() {
static Application singleton; 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() { void Application::exit_with_failure() {
exit(EXIT_FAILURE); // d'tor will be called automatically exit(EXIT_FAILURE);
} }

View File

@@ -29,10 +29,10 @@ class Application {
Application(); Application();
Application(const Application&); Application(const Application&);
~Application();
public: public:
static void initialize(); static void initialize();
static void free_resources();
static void exit_with_failure(); static void exit_with_failure();
}; };

View File

@@ -38,8 +38,8 @@ using namespace std;
const string BenchmarkPositions[] = { 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",
"r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
"4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -151,14 +151,19 @@ void benchmark(const string& commandLine) {
{ {
Move moves[1] = {MOVE_NONE}; Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0}; int dummy[2] = {0, 0};
Position pos(*it); Position pos(*it, 0);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl; cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (limitType == "perft") if (limitType == "perft")
totalNodes += perft(pos, maxDepth * OnePly); {
else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves)) int64_t perftCnt = perft(pos, maxDepth * OnePly);
cerr << "\nPerft " << maxDepth << " result (nodes searched): " << perftCnt << endl << endl;
totalNodes += perftCnt;
} else {
if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
break; break;
totalNodes += nodes_searched(); totalNodes += nodes_searched();
} }
}
cnt = get_system_time() - startTime; cnt = get_system_time() - startTime;
cerr << "===============================" cerr << "==============================="

View File

@@ -226,8 +226,9 @@ Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8]; Bitboard RayBB[64][8];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[2][64];
Bitboard OutpostMask[2][64]; Bitboard AttackSpanMask[2][64];
Bitboard BishopPseudoAttacks[64]; Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64]; Bitboard RookPseudoAttacks[64];
@@ -246,14 +247,12 @@ namespace {
void init_ray_bitboards(); void init_ray_bitboards();
void init_attacks(); void init_attacks();
void init_between_bitboards(); void init_between_bitboards();
void init_pseudo_attacks();
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);
Bitboard index_to_bitboard(int index, Bitboard mask); void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
void init_sliding_attacks(Bitboard attacks[], const int shift[], const Bitboard mult[], int deltas[][2]);
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]);
void init_pseudo_attacks();
} }
@@ -265,10 +264,13 @@ namespace {
/// standard output. This is sometimes useful for debugging. /// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) { void print_bitboard(Bitboard b) {
for(Rank r = RANK_8; r >= RANK_1; r--) {
for (Rank r = RANK_8; r >= RANK_1; r--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
for(File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' '; std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "|" << std::endl; std::cout << "|" << std::endl;
} }
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
@@ -279,8 +281,10 @@ void print_bitboard(Bitboard b) {
/// program initialization. /// program initialization.
void init_bitboards() { void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
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_ray_bitboards();
init_attacks(); init_attacks();
@@ -326,6 +330,7 @@ const int BitTable[64] = {
}; };
Square first_1(Bitboard b) { Square first_1(Bitboard b) {
b ^= (b - 1); b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32); uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]); return Square(BitTable[(fold * 0x783a9b23) >> 26]);
@@ -368,39 +373,6 @@ Square pop_1st_bit(Bitboard* bb) {
#endif #endif
// Optimized bitScanReverse32() implementation by Pascal Georges. Note
// that first bit is 1, this allow to differentiate between 0 and 1.
static CACHE_LINE_ALIGNMENT
const char MsbTable[256] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 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, 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, 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, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};
int bitScanReverse32(uint32_t b)
{
int result = 0;
if (b > 0xFFFF)
{
b >>= 16;
result += 16;
}
if (b > 0xFF)
{
b >>= 8;
result += 8;
}
return result + MsbTable[b];
}
namespace { namespace {
@@ -410,39 +382,46 @@ namespace {
// be necessary to touch any of them. // be necessary to touch any of them.
void init_masks() { void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL; SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE]; ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for(Square s = SQ_A1; s <= SQ_H8; s++) {
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = (1ULL << s); SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s]; ClearMaskBB[s] = ~SetMaskBB[s];
} }
for(Color c = WHITE; c <= BLACK; c++)
for(Square s = SQ_A1; s <= SQ_H8; s++) { for (Color c = WHITE; c <= BLACK; c++)
PassedPawnMask[c][s] = for (Square s = SQ_A1; s <= SQ_H8; s++)
in_front_bb(c, s) & this_and_neighboring_files_bb(s); {
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s); SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s);
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
} }
for (Bitboard b = 0ULL; b < 256ULL; b++) for (Bitboard b = 0ULL; b < 256ULL; b++)
BitCount8Bit[b] = (uint8_t)count_1s(b); BitCount8Bit[b] = (uint8_t)count_1s(b);
} }
int remove_bit_8(int i) { return ((i & ~15) >> 1) | (i & 7); }
void init_ray_bitboards() { 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[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
}
}
}
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() {
int i, j, k, l;
int step[16][8] = { const int step[16][8] = {
{0}, {0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0}, {7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0}, {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
@@ -450,102 +429,112 @@ namespace {
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8} {9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
}; };
for(i = 0; i < 64; i++) { for (int i = 0; i < 64; i++)
for(j = 0; j <= int(BK); j++) { for (int j = 0; j <= int(BK); j++)
{
StepAttackBB[j][i] = EmptyBoardBB; StepAttackBB[j][i] = EmptyBoardBB;
for(k = 0; k < 8 && step[j][k] != 0; k++) { for (int k = 0; k < 8 && step[j][k] != 0; k++)
l = i + step[j][k]; {
if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3) int l = i + step[j][k];
if (l >= 0 && l < 64 && abs((i & 7) - (l & 7)) < 3)
StepAttackBB[j][i] |= (1ULL << l); StepAttackBB[j][i] |= (1ULL << l);
} }
} }
} }
}
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=0, int fmax=7, int rmin=0, int rmax=7) { int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
Bitboard result = 0ULL; Bitboard result = 0ULL;
int rk = sq / 8, fl = sq % 8, r, f, i; int rk = sq / 8;
for(i = 0; i < dirs; i++) { int fl = sq % 8;
int dx = deltas[i][0], dy = deltas[i][1];
for(f = fl+dx, r = rk+dy; for (int i = 0; i < dirs; i++)
(dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax)); {
f += dx, r += dy) { int dx = deltas[i][0];
int dy = deltas[i][1];
int f = fl + dx;
int r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
{
result |= (1ULL << (f + r*8)); result |= (1ULL << (f + r*8));
if(block & (1ULL << (f + r*8))) break; if (block & (1ULL << (f + r*8)))
break;
f += dx;
r += dy;
} }
} }
return result; return result;
} }
void init_between_bitboards() { void init_between_bitboards() {
SquareDelta step[8] = {
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE const SquareDelta step[8] = { DELTA_E, DELTA_W, DELTA_N, DELTA_S,
}; DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE };
SignedDirection d;
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) { for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
BetweenBB[s1][s2] = EmptyBoardBB; BetweenBB[s1][s2] = EmptyBoardBB;
d = signed_direction_between_squares(s1, s2); SignedDirection d = signed_direction_between_squares(s1, s2);
if(d != SIGNED_DIR_NONE)
for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d]) if (d != SIGNED_DIR_NONE)
{
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) {
int i, j, bits = count_1s(mask);
Bitboard result = 0ULL; Bitboard result = 0ULL;
for(i = 0; i < bits; i++) { int bits = count_1s(mask);
j = pop_1st_bit(&mask);
if(index & (1 << i)) result |= (1ULL << j); for (int i = 0; i < bits; i++)
{
int j = pop_1st_bit(&mask);
if (index & (1 << i))
result |= (1ULL << j);
} }
return result; return result;
} }
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
void init_sliding_attacks(Bitboard attacks[], for (int i = 0, index = 0; i < 64; i++)
int attackIndex[], Bitboard mask[], {
const int shift[2], const Bitboard mult[],
int deltas[][2]) {
int i, j, k, index = 0;
Bitboard b;
for(i = 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, 0ULL, 4, deltas, 1, 6, 1, 6);
#if defined(IS_64BIT) #if defined(IS_64BIT)
j = (1 << (64 - shift[i])); int j = (1 << (64 - shift[i]));
#else #else
j = (1 << (32 - shift[i])); int j = (1 << (32 - shift[i]));
#endif #endif
for(k = 0; k < j; k++) { for (int k = 0; k < j; k++)
{
#if defined(IS_64BIT) #if defined(IS_64BIT)
b = index_to_bitboard(k, mask[i]); Bitboard b = index_to_bitboard(k, mask[i]);
attacks[index + ((b * mult[i]) >> shift[i])] = attacks[index + ((b * mult[i]) >> shift[i])] = sliding_attacks(i, b, 4, deltas);
sliding_attacks(i, b, 4, deltas);
#else #else
b = index_to_bitboard(k, mask[i]); Bitboard b = index_to_bitboard(k, mask[i]);
attacks[index + unsigned v = int(b) * int(mult[i]) ^ int(b >> 32) * int(mult[i] >> 32);
(unsigned(int(b) * int(mult[i]) ^ attacks[index + (v >> shift[i])] = sliding_attacks(i, b, 4, deltas);
int(b >> 32) * int(mult[i] >> 32))
>> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#endif #endif
} }
index += j; index += j;
} }
} }
void init_pseudo_attacks() { void init_pseudo_attacks() {
Square s;
for(s = SQ_A1; s <= SQ_H8; s++) { for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB); BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB); RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB); QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);

View File

@@ -74,8 +74,9 @@ extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8]; extern Bitboard RayBB[64][8];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard OutpostMask[2][64]; extern Bitboard AttackSpanMask[2][64];
extern const uint64_t RMult[64]; extern const uint64_t RMult[64];
extern const int RShift[64]; extern const int RShift[64];
@@ -127,9 +128,8 @@ inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb; *b ^= move_bb;
} }
/// rank_bb() and file_bb() gives a bitboard containing all squares on a given /// rank_bb() and file_bb() take a file or a square as input, and return
/// file or rank. It is also possible to pass a square as input to these /// a bitboard representing all squares on the given file or rank.
/// functions.
inline Bitboard rank_bb(Rank r) { inline Bitboard rank_bb(Rank r) {
return RankBB[r]; return RankBB[r];
@@ -156,7 +156,7 @@ inline Bitboard neighboring_files_bb(File f) {
} }
inline Bitboard neighboring_files_bb(Square s) { inline Bitboard neighboring_files_bb(Square s) {
return neighboring_files_bb(square_file(s)); return NeighboringFilesBB[square_file(s)];
} }
@@ -169,7 +169,7 @@ inline Bitboard this_and_neighboring_files_bb(File f) {
} }
inline Bitboard this_and_neighboring_files_bb(Square s) { inline Bitboard this_and_neighboring_files_bb(Square s) {
return this_and_neighboring_files_bb(square_file(s)); return ThisAndNeighboringFilesBB[square_file(s)];
} }
@@ -195,7 +195,7 @@ inline Bitboard in_front_bb(Color c, Rank r) {
} }
inline Bitboard in_front_bb(Color c, Square s) { inline Bitboard in_front_bb(Color c, Square s) {
return in_front_bb(c, square_rank(s)); return InFrontBB[c][square_rank(s)];
} }
@@ -208,7 +208,7 @@ inline Bitboard behind_bb(Color c, Rank r) {
} }
inline Bitboard behind_bb(Color c, Square s) { inline Bitboard behind_bb(Color c, Square s) {
return in_front_bb(opposite_color(c), square_rank(s)); return InFrontBB[opposite_color(c)][square_rank(s)];
} }
@@ -274,12 +274,11 @@ inline Bitboard squares_between(Square s1, Square s2) {
/// squares_in_front_of takes a color and a square as input, and returns a /// squares_in_front_of takes a color and a square as input, and returns a
/// bitboard representing all squares along the line in front of the square, /// bitboard representing all squares along the line in front of the square,
/// from the point of view of the given color. For instance, /// from the point of view of the given color. Definition of the table is:
/// squares_in_front_of(BLACK, SQ_E4) returns a bitboard with the squares /// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s)
/// e3, e2 and e1 set.
inline Bitboard squares_in_front_of(Color c, Square s) { inline Bitboard squares_in_front_of(Color c, Square s) {
return in_front_bb(c, s) & file_bb(s); return SquaresInFrontMask[c][s];
} }
@@ -287,33 +286,27 @@ inline Bitboard squares_in_front_of(Color c, Square s) {
/// behind the square instead of in front of the square. /// behind the square instead of in front of the square.
inline Bitboard squares_behind(Color c, Square s) { inline Bitboard squares_behind(Color c, Square s) {
return in_front_bb(opposite_color(c), s) & file_bb(s); return SquaresInFrontMask[opposite_color(c)][s];
} }
/// passed_pawn_mask takes a color and a square as input, and returns a /// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. /// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
} }
/// outpost_mask takes a color and a square as input, and returns a bitboard /// attack_span_mask takes a color and a square as input, and returns a bitboard
/// mask which can be used to test whether a piece on the square can possibly /// representing all squares that can be attacked by a pawn of the given color
/// be driven away by an enemy pawn. /// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
inline Bitboard outpost_mask(Color c, Square s) { inline Bitboard attack_span_mask(Color c, Square s) {
return OutpostMask[c][s]; return AttackSpanMask[c][s];
}
/// isolated_pawn_mask takes a square as input, and returns a bitboard mask
/// which can be used to test whether a pawn on the given square is isolated.
inline Bitboard isolated_pawn_mask(Square s) {
return neighboring_files_bb(s);
} }
@@ -349,7 +342,6 @@ extern Square pop_1st_bit(Bitboard* b);
extern void print_bitboard(Bitboard b); extern void print_bitboard(Bitboard b);
extern void init_bitboards(); extern void init_bitboards();
extern int bitScanReverse32(uint32_t b);
#endif // !defined(BITBOARD_H_INCLUDED) #endif // !defined(BITBOARD_H_INCLUDED)

View File

@@ -399,14 +399,15 @@ const string Book::file_name() { // Not const to compile on HP-UX 11.X
/// Book::get_move() gets a book move for a given position. Returns /// Book::get_move() gets a book move for a given position. Returns
/// MOVE_NONE if no book move is found. /// MOVE_NONE if no book move is found.
Move Book::get_move(const Position& pos) { Move Book::get_move(const Position& pos, bool findBestMove) {
if (!is_open() || bookSize == 0) if (!is_open() || bookSize == 0)
return MOVE_NONE; return MOVE_NONE;
int bookMove = 0, scoresSum = 0;
uint64_t key = book_key(pos);
BookEntry entry; BookEntry entry;
int bookMove = MOVE_NONE;
int scoresSum = 0, bestScore = 0;
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
for (int idx = find_key(key); idx < bookSize; idx++) for (int idx = find_key(key); idx < bookSize; idx++)
@@ -419,6 +420,17 @@ Move Book::get_move(const Position& pos) {
assert(score > 0); assert(score > 0);
// If findBestMove is true choose highest rated book move
if (findBestMove)
{
if (score > bestScore)
{
bestScore = score;
bookMove = entry.move;
}
continue;
}
// Choose book move according to its score. If a move has a very // Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with // 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.

View File

@@ -61,7 +61,7 @@ public:
void open(const std::string& fName); void open(const std::string& fName);
void close(); void close();
const std::string file_name(); const std::string file_name();
Move get_move(const Position& pos); Move get_move(const Position& pos, bool findBestMove);
private: private:
Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; } Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; }

View File

@@ -104,7 +104,7 @@ namespace {
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small. /// of the board, and for keeping the distance between the two kings small.
template<> template<>
Value EvaluationFunction<KXK>::apply(const Position& pos) { Value EvaluationFunction<KXK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == Value(0)); assert(pos.piece_count(weakerSide, PAWN) == Value(0));
@@ -130,7 +130,7 @@ Value EvaluationFunction<KXK>::apply(const Position& pos) {
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color. /// defending king towards a corner square of the right color.
template<> template<>
Value EvaluationFunction<KBNK>::apply(const Position& pos) { Value EvaluationFunction<KBNK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == Value(0));
assert(pos.piece_count(weakerSide, PAWN) == Value(0)); assert(pos.piece_count(weakerSide, PAWN) == Value(0));
@@ -159,7 +159,7 @@ Value EvaluationFunction<KBNK>::apply(const Position& pos) {
/// KP vs K. This endgame is evaluated with the help of a bitbase. /// KP vs K. This endgame is evaluated with the help of a bitbase.
template<> template<>
Value EvaluationFunction<KPK>::apply(const Position& pos) { Value EvaluationFunction<KPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == Value(0));
@@ -207,7 +207,7 @@ Value EvaluationFunction<KPK>::apply(const Position& pos) {
/// far advanced with support of the king, while the attacking king is far /// far advanced with support of the king, while the attacking king is far
/// away. /// away.
template<> template<>
Value EvaluationFunction<KRKP>::apply(const Position& pos) { Value EvaluationFunction<KRKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -264,7 +264,7 @@ Value EvaluationFunction<KRKP>::apply(const Position& pos) {
/// KR vs KB. This is very simple, and always returns drawish scores. The /// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge. /// score is slightly bigger when the defending king is close to the edge.
template<> template<>
Value EvaluationFunction<KRKB>::apply(const Position& pos) { Value EvaluationFunction<KRKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -280,7 +280,7 @@ Value EvaluationFunction<KRKB>::apply(const Position& pos) {
/// KR vs KN. The attacking side has slightly better winning chances than /// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart. /// in KR vs KB, particularly if the king and the knight are far apart.
template<> template<>
Value EvaluationFunction<KRKN>::apply(const Position& pos) { Value EvaluationFunction<KRKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -304,7 +304,7 @@ Value EvaluationFunction<KRKN>::apply(const Position& pos) {
/// for the defending side in the search, this is usually sufficient to be /// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR. /// able to win KQ vs KR.
template<> template<>
Value EvaluationFunction<KQKR>::apply(const Position& pos) { Value EvaluationFunction<KQKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -323,7 +323,7 @@ Value EvaluationFunction<KQKR>::apply(const Position& pos) {
} }
template<> template<>
Value EvaluationFunction<KBBKN>::apply(const Position& pos) { Value EvaluationFunction<KBBKN>::apply(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2); assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
@@ -352,12 +352,12 @@ Value EvaluationFunction<KBBKN>::apply(const Position& pos) {
/// K and two minors vs K and one or two minors or K and two knights against /// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw. /// king alone are always draw.
template<> template<>
Value EvaluationFunction<KmmKm>::apply(const Position&) { Value EvaluationFunction<KmmKm>::apply(const Position&) const {
return Value(0); return Value(0);
} }
template<> template<>
Value EvaluationFunction<KNNK>::apply(const Position&) { Value EvaluationFunction<KNNK>::apply(const Position&) const {
return Value(0); return Value(0);
} }
@@ -367,7 +367,7 @@ Value EvaluationFunction<KNNK>::apply(const Position&) {
/// 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<>
ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) { ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -421,7 +421,7 @@ ScaleFactor ScalingFunction<KBPsK>::apply(const Position& pos) {
/// It tests for fortress draws with a rook on the third rank defended by /// It tests for fortress draws with a rook on the third rank defended by
/// a pawn. /// a pawn.
template<> template<>
ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) { ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.piece_count(strongerSide, QUEEN) == 1);
@@ -452,7 +452,7 @@ ScaleFactor ScalingFunction<KQKRPs>::apply(const Position& pos) {
/// It would also be nice to rewrite the actual code for this function, /// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty. /// which is mostly copied from Glaurung 1.x, and not very pretty.
template<> template<>
ScaleFactor ScalingFunction<KRPKR>::apply(const Position &pos) { ScaleFactor ScalingFunction<KRPKR>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
@@ -570,7 +570,7 @@ ScaleFactor ScalingFunction<KRPKR>::apply(const Position &pos) {
/// single pattern: If the stronger side has no pawns and the defending king /// single pattern: If the stronger side has no pawns and the defending king
/// is actively placed, the position is drawish. /// is actively placed, the position is drawish.
template<> template<>
ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position &pos) { ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2); assert(pos.piece_count(strongerSide, PAWN) == 2);
@@ -609,7 +609,7 @@ ScaleFactor ScalingFunction<KRPPKRP>::apply(const Position &pos) {
/// against king. There is just a single rule here: If all pawns are on /// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw. /// the same rook file and are blocked by the defending king, it's a draw.
template<> template<>
ScaleFactor ScalingFunction<KPsK>::apply(const Position &pos) { ScaleFactor ScalingFunction<KPsK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.piece_count(strongerSide, PAWN) >= 2); assert(pos.piece_count(strongerSide, PAWN) >= 2);
@@ -655,7 +655,7 @@ ScaleFactor ScalingFunction<KPsK>::apply(const Position &pos) {
/// it's a draw. If the two bishops have opposite color, it's almost always /// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw. /// a draw.
template<> template<>
ScaleFactor ScalingFunction<KBPKB>::apply(const Position &pos) { ScaleFactor ScalingFunction<KBPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -708,7 +708,7 @@ ScaleFactor ScalingFunction<KBPKB>::apply(const Position &pos) {
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic /// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops. /// draws with opposite-colored bishops.
template<> template<>
ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) { ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -784,7 +784,7 @@ ScaleFactor ScalingFunction<KBPPKB>::apply(const Position& pos) {
/// square of the king is not of the same color as the stronger side's bishop, /// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. /// it's a draw.
template<> template<>
ScaleFactor ScalingFunction<KBPKN>::apply(const Position &pos) { ScaleFactor ScalingFunction<KBPKN>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -811,7 +811,7 @@ ScaleFactor ScalingFunction<KBPKN>::apply(const Position &pos) {
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents /// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn. /// the pawn from advancing, the position is drawn.
template<> template<>
ScaleFactor ScalingFunction<KNPK>::apply(const Position &pos) { 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);
@@ -841,7 +841,7 @@ ScaleFactor ScalingFunction<KNPK>::apply(const Position &pos) {
/// advanced and not on a rook file; in this case it is often possible to win /// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). /// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<> template<>
ScaleFactor ScalingFunction<KPKP>::apply(const Position &pos) { ScaleFactor ScalingFunction<KPKP>::apply(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == Value(0)); assert(pos.non_pawn_material(strongerSide) == Value(0));
assert(pos.non_pawn_material(weakerSide) == Value(0)); assert(pos.non_pawn_material(weakerSide) == Value(0));

View File

@@ -68,7 +68,7 @@ class EndgameFunctionBase {
public: public:
EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {} EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
virtual ~EndgameFunctionBase() {} virtual ~EndgameFunctionBase() {}
virtual T apply(const Position&) = 0; virtual T apply(const Position&) const = 0;
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
protected: protected:
@@ -85,14 +85,14 @@ template<EndgameType>
struct EvaluationFunction : public EndgameEvaluationFunctionBase { struct EvaluationFunction : public EndgameEvaluationFunctionBase {
typedef EndgameEvaluationFunctionBase Base; typedef EndgameEvaluationFunctionBase Base;
explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {} explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
Value apply(const Position&); Value apply(const Position&) const;
}; };
template<EndgameType> template<EndgameType>
struct ScalingFunction : public EndgameScalingFunctionBase { struct ScalingFunction : public EndgameScalingFunctionBase {
typedef EndgameScalingFunctionBase Base; typedef EndgameScalingFunctionBase Base;
explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {} explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {}
ScaleFactor apply(const Position&); ScaleFactor apply(const Position&) const;
}; };

View File

@@ -46,9 +46,11 @@ namespace {
const int GrainSize = 8; const int GrainSize = 8;
// Evaluation weights, initialized from UCI options // Evaluation weights, initialized from UCI options
Score WeightMobility, WeightPawnStructure; enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
Score WeightPassedPawns, WeightSpace; Score Weights[6];
Score WeightKingSafety[2];
typedef Value V;
#define S(mg, eg) make_score(mg, eg)
// Internal evaluation weights. These are applied on top of the evaluation // Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change // weights read from UCI parameters. The purpose is to be able to change
@@ -56,19 +58,9 @@ namespace {
// parameters at 100, which looks prettier. // parameters at 100, which looks prettier.
// //
// Values modified by Joona Kiiski // Values modified by Joona Kiiski
const Score WeightMobilityInternal = make_score(248, 271); const Score WeightsInternal[] = {
const Score WeightPawnStructureInternal = make_score(233, 201); S(248, 271), S(233, 201), S(252, 259), S(46, 0), S(247, 0), S(259, 0)
const Score WeightPassedPawnsInternal = make_score(252, 259); };
const Score WeightSpaceInternal = make_score( 46, 0);
const Score WeightKingSafetyInternal = make_score(247, 0);
const Score WeightKingOppSafetyInternal = make_score(259, 0);
// Mobility and outposts bonus modified by Joona Kiiski
typedef Value V;
#define S(mg, eg) make_score(mg, eg)
CACHE_LINE_ALIGNMENT
// Knight mobility bonus in middle game and endgame, indexed by the number // Knight mobility bonus in middle game and endgame, indexed by the number
// of attacked squares not occupied by friendly piecess. // of attacked squares not occupied by friendly piecess.
@@ -138,28 +130,22 @@ namespace {
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0) // 8 V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0) // 8
}; };
// ThreatBonus[][] contains bonus according to which piece type // ThreatBonus[attacking][attacked] contains bonus according to which
// attacks which one. // piece type attacks which one.
#define Z S(0, 0)
const Score ThreatBonus[8][8] = { const Score ThreatBonus[8][8] = {
{ Z, Z, Z, Z, Z, Z, Z, Z }, // not used {}, {},
{ Z, S(18,37), Z, S(37,47), S(55,97), S(55,97), Z, Z }, // KNIGHT attacks { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
{ Z, S(18,37), S(37,47), Z, S(55,97), S(55,97), Z, Z }, // BISHOP attacks { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
{ Z, S( 9,27), S(27,47), S(27,47), Z, S(37,47), Z, Z }, // ROOK attacks { S(0, 0), S(-1, 29), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK
{ Z, S(27,37), S(27,37), S(27,37), S(27,37), Z, Z, Z }, // QUEEN attacks { S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
{ Z, Z, Z, Z, Z, Z, Z, Z }, // not used
{ Z, Z, Z, Z, Z, Z, Z, Z }, // not used
{ Z, Z, Z, Z, Z, Z, Z, Z } // not used
}; };
// ThreatedByPawnPenalty[] contains a penalty according to which piece // ThreatedByPawnPenalty[] contains a penalty according to which piece
// type is attacked by an enemy pawn. // type is attacked by an enemy pawn.
const Score ThreatedByPawnPenalty[8] = { const Score ThreatedByPawnPenalty[8] = {
Z, Z, S(56, 70), S(56, 70), S(76, 99), S(86, 118), Z, Z S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
}; };
#undef Z
#undef S #undef S
// Bonus for unstoppable passed pawns // Bonus for unstoppable passed pawns
@@ -211,20 +197,15 @@ namespace {
(1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5) (1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
}; };
/// King safety constants and variables. The king safety scores are taken /// King danger constants and variables. The king danger scores are taken
/// from the array SafetyTable[]. Various little "meta-bonuses" measuring /// from the KingDangerTable[]. Various little "meta-bonuses" measuring
/// the strength of the attack are added up into an integer, which is used /// the strength of the enemy attack are added up into an integer, which
/// as an index to SafetyTable[]. /// is used as an index to KingDangerTable[].
// Attack weights for each piece type and table indexed on piece type // KingAttackWeights[] contains king attack weights by piece type
const int QueenAttackWeight = 5; const int KingAttackWeights[8] = { 0, 0, 2, 2, 3, 5 };
const int RookAttackWeight = 3;
const int BishopAttackWeight = 2;
const int KnightAttackWeight = 2;
const int AttackWeight[] = { 0, 0, KnightAttackWeight, BishopAttackWeight, RookAttackWeight, QueenAttackWeight }; // Bonuses for enemy's safe checks
// Bonuses for safe checks
const int QueenContactCheckBonus = 3; const int QueenContactCheckBonus = 3;
const int DiscoveredCheckBonus = 3; const int DiscoveredCheckBonus = 3;
const int QueenCheckBonus = 2; const int QueenCheckBonus = 2;
@@ -232,12 +213,6 @@ namespace {
const int BishopCheckBonus = 1; const int BishopCheckBonus = 1;
const int KnightCheckBonus = 1; const int KnightCheckBonus = 1;
// Scan for queen contact mates?
const bool QueenContactMates = true;
// Bonus for having a mate threat
const int MateThreatBonus = 3;
// InitKingDanger[] contains bonuses based on the position of the defending // InitKingDanger[] contains bonuses based on the position of the defending
// king. // king.
const int InitKingDanger[64] = { const int InitKingDanger[64] = {
@@ -251,9 +226,8 @@ namespace {
15, 15, 15, 15, 15, 15, 15, 15 15, 15, 15, 15, 15, 15, 15, 15
}; };
// SafetyTable[] contains the actual king safety scores. It is initialized // KingDangerTable[color][] contains the actual king danger weighted scores
// in init_safety(). Score KingDangerTable[2][128];
Value SafetyTable[100];
// Pawn and material hash tables, indexed by the current thread id. // Pawn and material hash tables, indexed by the current thread id.
// Note that they will be initialized at 0 being global variables. // Note that they will be initialized at 0 being global variables.
@@ -266,7 +240,10 @@ namespace {
// Function prototypes // Function prototypes
template<bool HasPopCnt> template<bool HasPopCnt>
Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID); Value do_evaluate(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt>
void init_attack_tables(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
void evaluate_pieces_of_color(const Position& pos, EvalInfo& ei); void evaluate_pieces_of_color(const Position& pos, EvalInfo& ei);
@@ -278,9 +255,12 @@ namespace {
void evaluate_threats(const Position& pos, EvalInfo& ei); void evaluate_threats(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
void evaluate_space(const Position& pos, EvalInfo& ei); int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us>
void evaluate_passed_pawns(const Position& pos, EvalInfo& ei); void evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
void evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
void evaluate_trapped_bishop_a7h7(const Position& pos, Square s, Color us, EvalInfo& ei); void evaluate_trapped_bishop_a7h7(const Position& pos, Square s, Color us, EvalInfo& ei);
void evaluate_trapped_bishop_a1h1(const Position& pos, Square s, Color us, EvalInfo& ei); void evaluate_trapped_bishop_a1h1(const Position& pos, Square s, Color us, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight); inline Score apply_weight(Score v, Score weight);
@@ -297,19 +277,21 @@ namespace {
/// evaluate() is the main evaluation function. It always computes two /// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates /// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material. /// between them based on the remaining material.
Value evaluate(const Position& pos, EvalInfo& ei, int threadID) { Value evaluate(const Position& pos, EvalInfo& ei) {
return CpuHasPOPCNT ? do_evaluate<true>(pos, ei, threadID) return CpuHasPOPCNT ? do_evaluate<true>(pos, ei)
: do_evaluate<false>(pos, ei, threadID); : do_evaluate<false>(pos, ei);
} }
namespace { namespace {
template<bool HasPopCnt> template<bool HasPopCnt>
Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) { Value do_evaluate(const Position& pos, EvalInfo& ei) {
ScaleFactor factor[2];
assert(pos.is_ok()); assert(pos.is_ok());
assert(threadID >= 0 && threadID < MAX_THREADS); assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.is_check()); assert(!pos.is_check());
memset(&ei, 0, sizeof(EvalInfo)); memset(&ei, 0, sizeof(EvalInfo));
@@ -319,7 +301,7 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
ei.value = pos.value(); ei.value = pos.value();
// Probe the material hash table // Probe the material hash table
ei.mi = MaterialTable[threadID]->get_material_info(pos); ei.mi = MaterialTable[pos.thread()]->get_material_info(pos);
ei.value += ei.mi->material_value(); ei.value += ei.mi->material_value();
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
@@ -328,30 +310,16 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
return ei.mi->evaluate(pos); return ei.mi->evaluate(pos);
// After get_material_info() call that modifies them // After get_material_info() call that modifies them
ScaleFactor factor[2];
factor[WHITE] = ei.mi->scale_factor(pos, WHITE); factor[WHITE] = ei.mi->scale_factor(pos, WHITE);
factor[BLACK] = ei.mi->scale_factor(pos, BLACK); factor[BLACK] = ei.mi->scale_factor(pos, BLACK);
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = PawnTable[threadID]->get_pawn_info(pos); ei.pi = PawnTable[pos.thread()]->get_pawn_info(pos);
ei.value += apply_weight(ei.pi->pawns_value(), WeightPawnStructure); ei.value += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
// Initialize king attack bitboards and king attack zones for both sides // Initialize attack bitboards with pawns evaluation
ei.attackedBy[WHITE][KING] = pos.attacks_from<KING>(pos.king_square(WHITE)); init_attack_tables<WHITE, HasPopCnt>(pos, ei);
ei.attackedBy[BLACK][KING] = pos.attacks_from<KING>(pos.king_square(BLACK)); init_attack_tables<BLACK, HasPopCnt>(pos, ei);
ei.kingZone[WHITE] = ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8);
ei.kingZone[BLACK] = ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8);
// Initialize pawn attack bitboards for both sides
ei.attackedBy[WHITE][PAWN] = ei.pi->pawn_attacks(WHITE);
ei.attackedBy[BLACK][PAWN] = ei.pi->pawn_attacks(BLACK);
Bitboard b1 = ei.attackedBy[WHITE][PAWN] & ei.attackedBy[BLACK][KING];
Bitboard b2 = ei.attackedBy[BLACK][PAWN] & ei.attackedBy[WHITE][KING];
if (b1)
ei.kingAttackersCount[WHITE] = count_1s_max_15<HasPopCnt>(b1)/2;
if (b2)
ei.kingAttackersCount[BLACK] = count_1s_max_15<HasPopCnt>(b2)/2;
// Evaluate pieces // Evaluate pieces
evaluate_pieces_of_color<WHITE, HasPopCnt>(pos, ei); evaluate_pieces_of_color<WHITE, HasPopCnt>(pos, ei);
@@ -363,23 +331,24 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
evaluate_king<WHITE, HasPopCnt>(pos, ei); evaluate_king<WHITE, HasPopCnt>(pos, ei);
evaluate_king<BLACK, HasPopCnt>(pos, ei); evaluate_king<BLACK, HasPopCnt>(pos, ei);
// Evaluate tactical threats, we need full attack info // Evaluate tactical threats, we need full attack info including king
evaluate_threats<WHITE>(pos, ei); evaluate_threats<WHITE>(pos, ei);
evaluate_threats<BLACK>(pos, ei); evaluate_threats<BLACK>(pos, ei);
// Evaluate passed pawns. We evaluate passed pawns for both sides at once, // Evaluate passed pawns, we need full attack info including king
// because we need to know which side promotes first in positions where evaluate_passed_pawns<WHITE>(pos, ei);
// both sides have an unstoppable passed pawn. To be called after all attacks evaluate_passed_pawns<BLACK>(pos, ei);
// are computed, included king.
if (ei.pi->passed_pawns()) // If one side has only a king, check whether exsists any unstoppable passed pawn
evaluate_passed_pawns(pos, ei); if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
evaluate_unstoppable_pawns(pos, ei);
Phase phase = ei.mi->game_phase(); Phase phase = ei.mi->game_phase();
// Middle-game specific evaluation terms // Middle-game specific evaluation terms
if (phase > PHASE_ENDGAME) if (phase > PHASE_ENDGAME)
{ {
// Pawn storms in positions with opposite castling. // Pawn storms in positions with opposite castling
if ( square_file(pos.king_square(WHITE)) >= FILE_E if ( square_file(pos.king_square(WHITE)) >= FILE_E
&& square_file(pos.king_square(BLACK)) <= FILE_D) && square_file(pos.king_square(BLACK)) <= FILE_D)
@@ -393,13 +362,13 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
// Evaluate space for both sides // Evaluate space for both sides
if (ei.mi->space_weight() > 0) if (ei.mi->space_weight() > 0)
{ {
evaluate_space<WHITE, HasPopCnt>(pos, ei); int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei);
evaluate_space<BLACK, HasPopCnt>(pos, ei); ei.value += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]);
} }
} }
// Mobility // Mobility
ei.value += apply_weight(ei.mobility, WeightMobility); ei.value += apply_weight(ei.mobility, Weights[Mobility]);
// If we don't already have an unusual scale factor, check for opposite // If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those // colored bishop endgames, and use a lower scale for those
@@ -431,11 +400,7 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
} }
// Interpolate between the middle game and the endgame score // Interpolate between the middle game and the endgame score
Color stm = pos.side_to_move(); return Sign[pos.side_to_move()] * scale_by_game_phase(ei.value, phase, factor);
Value v = Sign[stm] * scale_by_game_phase(ei.value, phase, factor);
return (ei.mateThreat[stm] == MOVE_NONE ? v : 8 * QueenValueMidgame - v);
} }
} // namespace } // namespace
@@ -482,28 +447,46 @@ void quit_eval() {
void read_weights(Color us) { void read_weights(Color us) {
Color them = opposite_color(us); // King safety is asymmetrical. Our king danger level is weighted by
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
const int kingDangerUs = (us == WHITE ? KingDangerUs : KingDangerThem);
const int kingDangerThem = (us == WHITE ? KingDangerThem : KingDangerUs);
WeightMobility = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightMobilityInternal); Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
WeightPawnStructure = weight_option("Pawn Structure (Middle Game)", "Pawn Structure (Endgame)", WeightPawnStructureInternal); Weights[PawnStructure] = weight_option("Pawn Structure (Middle Game)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
WeightPassedPawns = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightPassedPawnsInternal); Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
WeightSpace = weight_option("Space", "Space", WeightSpaceInternal); Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
WeightKingSafety[us] = weight_option("Cowardice", "Cowardice", WeightKingSafetyInternal); Weights[kingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
WeightKingSafety[them] = weight_option("Aggressiveness", "Aggressiveness", WeightKingOppSafetyInternal); Weights[kingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
// If running in analysis mode, make sure we use symmetrical king safety. We do this // If running in analysis mode, make sure we use symmetrical king safety. We do this
// by replacing both WeightKingSafety[us] and WeightKingSafety[them] by their average. // by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (get_option_value_bool("UCI_AnalyseMode")) if (get_option_value_bool("UCI_AnalyseMode"))
{ Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
WeightKingSafety[us] = (WeightKingSafety[us] + WeightKingSafety[them]) / 2;
WeightKingSafety[them] = WeightKingSafety[us];
}
init_safety(); init_safety();
} }
namespace { namespace {
// init_attack_tables() initializes king bitboards for both sides adding
// pawn attacks. To be done before other evaluations.
template<Color Us, bool HasPopCnt>
void init_attack_tables(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8));
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
b &= ei.attackedBy[Us][PAWN];
if (b)
ei.kingAttackersCount[Us] = count_1s_max_15<HasPopCnt>(b) / 2;
}
// evaluate_outposts() evaluates bishop and knight outposts squares // evaluate_outposts() evaluates bishop and knight outposts squares
template<PieceType Piece, Color Us> template<PieceType Piece, Color Us>
@@ -561,7 +544,7 @@ namespace {
if (b & ei.kingZone[Us]) if (b & ei.kingZone[Us])
{ {
ei.kingAttackersCount[Us]++; ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += AttackWeight[Piece]; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]); Bitboard bb = (b & ei.attackedBy[Them][KING]);
if (bb) if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += count_1s_max_15<HasPopCnt>(bb); ei.kingAdjacentZoneAttacksCount[Us] += count_1s_max_15<HasPopCnt>(bb);
@@ -579,7 +562,7 @@ namespace {
ei.value -= Sign[Us] * ThreatedByPawnPenalty[Piece]; ei.value -= Sign[Us] * ThreatedByPawnPenalty[Piece];
// Bishop and knight outposts squares // Bishop and knight outposts squares
if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Them)) if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Us))
evaluate_outposts<Piece, Us>(pos, ei, s); evaluate_outposts<Piece, Us>(pos, ei, s);
// Special patterns: trapped bishops on a7/h7/a2/h2 // Special patterns: trapped bishops on a7/h7/a2/h2
@@ -707,16 +690,15 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard undefended, attackedByOthers, escapeSquares, occ, b, b2, safe; Bitboard undefended, b, b1, b2, safe;
Square from, to;
bool sente; bool sente;
int attackUnits, count, shelter = 0; int attackUnits, shelter = 0;
const Square s = pos.king_square(Us); const Square ksq = pos.king_square(Us);
// King shelter // King shelter
if (relative_rank(Us, s) <= RANK_4) if (relative_rank(Us, ksq) <= RANK_4)
{ {
shelter = ei.pi->get_king_shelter(pos, Us, s); shelter = ei.pi->get_king_shelter(pos, Us, ksq);
ei.value += Sign[Us] * make_score(shelter, 0); ei.value += Sign[Us] * make_score(shelter, 0);
} }
@@ -738,245 +720,140 @@ namespace {
| ei.attacked_by(Us, QUEEN)); | ei.attacked_by(Us, QUEEN));
// Initialize the 'attackUnits' variable, which is used later on as an // Initialize the 'attackUnits' variable, which is used later on as an
// index to the SafetyTable[] array. The initial value is based on the // index to the KingDangerTable[] array. The initial value is based on
// number and types of the attacking pieces, the number of attacked and // the number and types of the enemy's attacking pieces, the number of
// undefended squares around the king, the square of the king, and the // attacked and undefended squares around our king, the square of the
// quality of the pawn shelter. // king, and the quality of the pawn shelter.
attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s_max_15<HasPopCnt>(undefended)) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s_max_15<HasPopCnt>(undefended))
+ InitKingDanger[relative_square(Us, s)] + InitKingDanger[relative_square(Us, ksq)]
- (shelter >> 5); - shelter / 32;
// Analyse safe queen contact checks // Analyse enemy's safe queen contact checks. First find undefended
// squares around the king attacked by enemy queen...
b = undefended & ei.attacked_by(Them, QUEEN) & ~pos.pieces_of_color(Them); b = undefended & ei.attacked_by(Them, QUEEN) & ~pos.pieces_of_color(Them);
if (b) if (b)
{ {
attackedByOthers = ei.attacked_by(Them, PAWN) | ei.attacked_by(Them, KNIGHT) // ...then remove squares not supported by another enemy piece
| ei.attacked_by(Them, BISHOP) | ei.attacked_by(Them, ROOK); b &= ( ei.attacked_by(Them, PAWN) | ei.attacked_by(Them, KNIGHT)
| ei.attacked_by(Them, BISHOP) | ei.attacked_by(Them, ROOK));
b &= attackedByOthers;
// Squares attacked by the queen and supported by another enemy piece and
// not defended by other pieces but our king.
if (b) if (b)
{ attackUnits += QueenContactCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1);
// The bitboard b now contains the squares available for safe queen
// contact checks.
count = count_1s_max_15<HasPopCnt>(b);
attackUnits += QueenContactCheckBonus * count * (sente ? 2 : 1);
// Is there a mate threat?
if (QueenContactMates && !pos.is_check())
{
escapeSquares = pos.attacks_from<KING>(s) & ~pos.pieces_of_color(Us) & ~attackedByOthers;
occ = pos.occupied_squares();
while (b)
{
to = pop_1st_bit(&b);
// Do we have escape squares from queen contact check attack ?
if (!(escapeSquares & ~queen_attacks_bb(to, occ & ClearMaskBB[s])))
{
// We have a mate, unless the queen is pinned or there
// is an X-ray attack through the queen.
for (int i = 0; i < pos.piece_count(Them, QUEEN); i++)
{
from = pos.piece_list(Them, QUEEN, i);
if ( bit_is_set(pos.attacks_from<QUEEN>(from), to)
&& !bit_is_set(pos.pinned_pieces(Them), from)
&& !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(ROOK, QUEEN, Us))
&& !(bishop_attacks_bb(to, occ & ClearMaskBB[from]) & pos.pieces(BISHOP, QUEEN, Us)))
// Set the mate threat move
ei.mateThreat[Them] = make_move(from, to);
}
}
}
}
}
} }
// Analyse safe distance checks // Analyse enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces_of_color(Them) | ei.attacked_by(Us)); safe = ~(pos.pieces_of_color(Them) | ei.attacked_by(Us));
if (QueenCheckBonus > 0 || RookCheckBonus > 0) b1 = pos.attacks_from<ROOK>(ksq) & safe;
{ b2 = pos.attacks_from<BISHOP>(ksq) & safe;
b = pos.attacks_from<ROOK>(s) & safe;
// Queen checks // Enemy queen safe checks
b2 = b & ei.attacked_by(Them, QUEEN); b = (b1 | b2) & ei.attacked_by(Them, QUEEN);
if (b2)
attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b2);
// Rook checks
b2 = b & ei.attacked_by(Them, ROOK);
if (b2)
attackUnits += RookCheckBonus * count_1s_max_15<HasPopCnt>(b2);
}
if (QueenCheckBonus > 0 || BishopCheckBonus > 0)
{
b = pos.attacks_from<BISHOP>(s) & safe;
// Queen checks
b2 = b & ei.attacked_by(Them, QUEEN);
if (b2)
attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b2);
// Bishop checks
b2 = b & ei.attacked_by(Them, BISHOP);
if (b2)
attackUnits += BishopCheckBonus * count_1s_max_15<HasPopCnt>(b2);
}
if (KnightCheckBonus > 0)
{
b = pos.attacks_from<KNIGHT>(s) & safe;
// Knight checks
b2 = b & ei.attacked_by(Them, KNIGHT);
if (b2)
attackUnits += KnightCheckBonus * count_1s_max_15<HasPopCnt>(b2);
}
// Analyse discovered checks (only for non-pawns right now, consider
// adding pawns later).
if (DiscoveredCheckBonus)
{
b = pos.discovered_check_candidates(Them) & ~pos.pieces(PAWN);
if (b) if (b)
attackUnits += DiscoveredCheckBonus * count_1s_max_15<HasPopCnt>(b) * (sente ? 2 : 1); attackUnits += QueenCheckBonus * count_1s_max_15<HasPopCnt>(b);
}
// Has a mate threat been found? We don't do anything here if the // Enemy rooks safe checks
// side with the mating move is the side to move, because in that b = b1 & ei.attacked_by(Them, ROOK);
// case the mating side will get a huge bonus at the end of the main if (b)
// evaluation function instead. attackUnits += RookCheckBonus * count_1s_max_15<HasPopCnt>(b);
if (ei.mateThreat[Them] != MOVE_NONE)
attackUnits += MateThreatBonus;
// Ensure that attackUnits is between 0 and 99, in order to avoid array // Enemy bishops safe checks
// out of bounds errors. b = b2 & ei.attacked_by(Them, BISHOP);
if (b)
attackUnits += BishopCheckBonus * count_1s_max_15<HasPopCnt>(b);
// Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attacked_by(Them, KNIGHT) & safe;
if (b)
attackUnits += KnightCheckBonus * count_1s_max_15<HasPopCnt>(b);
// To index KingDangerTable[] attackUnits must be in [0, 99] range
attackUnits = Min(99, Max(0, attackUnits)); attackUnits = Min(99, Max(0, attackUnits));
// Finally, extract the king safety score from the SafetyTable[] array. // Finally, extract the king danger score from the KingDangerTable[]
// Add the score to the evaluation, and also to ei.futilityMargin. The // array and subtract the score from evaluation. Set also ei.kingDanger[]
// reason for adding the king safety score to the futility margin is // value that will be used for pruning because this value can sometimes
// that the king safety scores can sometimes be very big, and that // be very big, and so capturing a single attacking piece can therefore
// capturing a single attacking piece can therefore result in a score // result in a score change far bigger than the value of the captured piece.
// change far bigger than the value of the captured piece. ei.value -= Sign[Us] * KingDangerTable[Us][attackUnits];
Score v = apply_weight(make_score(SafetyTable[attackUnits], 0), WeightKingSafety[Us]); ei.kingDanger[Us] = mg_value(KingDangerTable[Us][attackUnits]);
ei.value -= Sign[Us] * v;
ei.futilityMargin[Us] += mg_value(v);
} }
} }
// evaluate_passed_pawns_of_color() evaluates the passed pawns of the given color // evaluate_passed_pawns<>() evaluates the passed pawns of the given color
template<Color Us> template<Color Us>
void evaluate_passed_pawns_of_color(const Position& pos, int movesToGo[], Square pawnToGo[], EvalInfo& ei) { void evaluate_passed_pawns(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b2, b3, b4; Bitboard squaresToQueen, defendedSquares, unsafeSquares, supportingPawns;
Square ourKingSq = pos.king_square(Us); Bitboard b = ei.pi->passed_pawns() & pos.pieces_of_color(Us);
Square theirKingSq = pos.king_square(Them);
Bitboard b = ei.pi->passed_pawns() & pos.pieces(PAWN, Us);
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); Square s = pop_1st_bit(&b);
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
assert(pos.pawn_is_passed(Us, s)); assert(pos.pawn_is_passed(Us, s));
int r = int(relative_rank(Us, s) - RANK_2); int r = int(relative_rank(Us, s) - RANK_2);
int tr = Max(0, r * (r - 1)); int tr = r * (r - 1);
// Base bonus based on rank // Base bonus based on rank
Value mbonus = Value(20 * tr); Value mbonus = Value(20 * tr);
Value ebonus = Value(10 + r * r * 10); Value ebonus = Value(10 + r * r * 10);
// Adjust bonus based on king proximity
if (tr) if (tr)
{ {
Square blockSq = s + pawn_push(Us); Square blockSq = s + pawn_push(Us);
ebonus -= Value(square_distance(ourKingSq, blockSq) * 3 * tr); // Adjust bonus based on kings proximity
ebonus -= Value(square_distance(ourKingSq, blockSq + pawn_push(Us)) * 1 * tr); ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * tr);
ebonus += Value(square_distance(theirKingSq, blockSq) * 6 * tr); ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * 1 * tr);
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 6 * tr);
// If the pawn is free to advance, increase bonus // If the pawn is free to advance, increase bonus
if (pos.square_is_empty(blockSq)) if (pos.square_is_empty(blockSq))
{ {
// There are no enemy pawns in the pawn's path squaresToQueen = squares_in_front_of(Us, s);
b2 = squares_in_front_of(Us, s); defendedSquares = squaresToQueen & ei.attacked_by(Us);
assert((b2 & pos.pieces(PAWN, Them)) == EmptyBoardBB);
// Squares attacked by us
b4 = b2 & ei.attacked_by(Us);
// Squares attacked or occupied by enemy pieces
b3 = b2 & (ei.attacked_by(Them) | pos.pieces_of_color(Them));
// If there is an enemy rook or queen attacking the pawn from behind, // If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. // add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy.
if ( (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them)) if ( (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them))
&& (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<QUEEN>(s))) && (squares_behind(Us, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<QUEEN>(s)))
b3 = b2; unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attacked_by(Them) | pos.pieces_of_color(Them));
// Are any of the squares in the pawn's path attacked or occupied by the enemy? // If there aren't enemy attacks or pieces along the path to queen give
if (b3 == EmptyBoardBB) // huge bonus. Even bigger if we protect the pawn's path.
// No enemy attacks or pieces, huge bonus! if (!unsafeSquares)
// Even bigger if we protect the pawn's path ebonus += Value(tr * (squaresToQueen == defendedSquares ? 17 : 15));
ebonus += Value(tr * (b2 == b4 ? 17 : 15));
else else
// OK, there are enemy attacks or pieces (but not pawns). Are those // OK, there are enemy attacks or pieces (but not pawns). Are those
// squares which are attacked by the enemy also attacked by us ? // squares which are attacked by the enemy also attacked by us ?
// If yes, big bonus (but smaller than when there are no enemy attacks), // If yes, big bonus (but smaller than when there are no enemy attacks),
// if no, somewhat smaller bonus. // if no, somewhat smaller bonus.
ebonus += Value(tr * ((b3 & b4) == b3 ? 13 : 8)); ebonus += Value(tr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8));
// At last, add a small bonus when there are no *friendly* pieces // At last, add a small bonus when there are no *friendly* pieces
// in the pawn's path. // in the pawn's path.
if ((b2 & pos.pieces_of_color(Us)) == EmptyBoardBB) if (!(squaresToQueen & pos.pieces_of_color(Us)))
ebonus += Value(tr); ebonus += Value(tr);
} }
} // tr != 0 } // tr != 0
// If the pawn is supported by a friendly pawn, increase bonus // Increase the bonus if the passed pawn is supported by a friendly pawn
b2 = pos.pieces(PAWN, Us) & neighboring_files_bb(s); // on the same rank and a bit smaller if it's on the previous rank.
if (b2 & rank_bb(s)) supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(s);
if (supportingPawns & rank_bb(s))
ebonus += Value(r * 20); ebonus += Value(r * 20);
else if (pos.attacks_from<PAWN>(s, Them) & b2) else if (supportingPawns & rank_bb(s - pawn_push(Us)))
ebonus += Value(r * 12); ebonus += Value(r * 12);
// If the other side has only a king, check whether the pawn is
// unstoppable
if (pos.non_pawn_material(Them) == Value(0))
{
Square qsq;
int d;
qsq = relative_square(Us, make_square(square_file(s), RANK_8));
d = square_distance(s, qsq)
- square_distance(theirKingSq, qsq)
+ (Us != pos.side_to_move());
if (d < 0)
{
int mtg = RANK_8 - relative_rank(Us, s);
int blockerCount = count_1s_max_15(squares_in_front_of(Us,s) & pos.occupied_squares());
mtg += blockerCount;
d += blockerCount;
if (d < 0 && (!movesToGo[Us] || movesToGo[Us] > mtg))
{
movesToGo[Us] = mtg;
pawnToGo[Us] = s;
}
}
}
// Rook pawns are a special case: They are sometimes worse, and // Rook pawns are a special case: They are sometimes worse, and
// sometimes better than other passed pawns. It is difficult to find // sometimes better than other passed pawns. It is difficult to find
// good rules for determining whether they are good or bad. For now, // good rules for determining whether they are good or bad. For now,
@@ -992,23 +869,54 @@ namespace {
ebonus -= ebonus / 4; ebonus -= ebonus / 4;
} }
// Add the scores for this pawn to the middle game and endgame eval. // Add the scores for this pawn to the middle game and endgame eval
ei.value += Sign[Us] * apply_weight(make_score(mbonus, ebonus), WeightPassedPawns); ei.value += Sign[Us] * apply_weight(make_score(mbonus, ebonus), Weights[PassedPawns]);
} // while } // while
} }
// evaluate_passed_pawns() evaluates the passed pawns for both sides // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides
void evaluate_passed_pawns(const Position& pos, EvalInfo& ei) { void evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
int movesToGo[2] = {0, 0}; int movesToGo[2] = {0, 0};
Square pawnToGo[2] = {SQ_NONE, SQ_NONE}; Square pawnToGo[2] = {SQ_NONE, SQ_NONE};
// Evaluate pawns for each color for (Color c = WHITE; c <= BLACK; c++)
evaluate_passed_pawns_of_color<WHITE>(pos, movesToGo, pawnToGo, ei); {
evaluate_passed_pawns_of_color<BLACK>(pos, movesToGo, pawnToGo, ei); // Skip evaluation if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c)))
continue;
Bitboard b = ei.pi->passed_pawns() & pos.pieces_of_color(c);
while (b)
{
Square s = pop_1st_bit(&b);
Square queeningSquare = relative_square(c, make_square(square_file(s), RANK_8));
int d = square_distance(s, queeningSquare)
- (relative_rank(c, s) == RANK_2) // Double pawn push
- square_distance(pos.king_square(opposite_color(c)), queeningSquare)
+ int(c != pos.side_to_move());
// Do we protect the path to queening ?
bool pathDefended = (ei.attacked_by(c) & squares_in_front_of(c, s)) == squares_in_front_of(c, s);
if (d < 0 || pathDefended)
{
int mtg = RANK_8 - relative_rank(c, s);
int blockerCount = count_1s_max_15(squares_in_front_of(c, s) & pos.occupied_squares());
mtg += blockerCount;
d += blockerCount;
if ((d < 0 || pathDefended) && (!movesToGo[c] || movesToGo[c] > mtg))
{
movesToGo[c] = mtg;
pawnToGo[c] = s;
}
}
}
}
// Neither side has an unstoppable passed pawn? // Neither side has an unstoppable passed pawn?
if (!(movesToGo[WHITE] | movesToGo[BLACK])) if (!(movesToGo[WHITE] | movesToGo[BLACK]))
@@ -1131,29 +1039,24 @@ namespace {
// twice. Finally, the space bonus is scaled by a weight taken from the // twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. // material hash table.
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
void evaluate_space(const Position& pos, EvalInfo& ei) { int evaluate_space(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
// Find the safe squares for our pieces inside the area defined by // Find the safe squares for our pieces inside the area defined by
// SpaceMask[us]. A square is unsafe if it is attacked by an enemy // SpaceMask[us]. A square is unsafe if it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece. // pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safe = SpaceMask[Us]
Bitboard safeSquares = SpaceMask[Us]
& ~pos.pieces(PAWN, Us) & ~pos.pieces(PAWN, Us)
& ~ei.attacked_by(Them, PAWN) & ~ei.attacked_by(Them, PAWN)
& ~(~ei.attacked_by(Us) & ei.attacked_by(Them)); & (ei.attacked_by(Us) | ~ei.attacked_by(Them));
// Find all squares which are at most three squares behind some friendly // Find all squares which are at most three squares behind some friendly pawn
// pawn. Bitboard behind = pos.pieces(PAWN, Us);
Bitboard behindFriendlyPawns = pos.pieces(PAWN, Us); behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behindFriendlyPawns |= (Us == WHITE ? behindFriendlyPawns >> 8 : behindFriendlyPawns << 8); behind |= (Us == WHITE ? behind >> 16 : behind << 16);
behindFriendlyPawns |= (Us == WHITE ? behindFriendlyPawns >> 16 : behindFriendlyPawns << 16);
int space = count_1s_max_15<HasPopCnt>(safeSquares) return count_1s_max_15<HasPopCnt>(safe) + count_1s_max_15<HasPopCnt>(behind & safe);
+ count_1s_max_15<HasPopCnt>(behindFriendlyPawns & safeSquares);
ei.value += Sign[Us] * apply_weight(make_score(space * ei.mi->space_weight(), 0), WeightSpace);
} }
@@ -1186,17 +1089,11 @@ namespace {
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
Score uciWeight = make_score(get_option_value_int(mgOpt), get_option_value_int(egOpt)); // Scale option value from 100 to 256
int mg = get_option_value_int(mgOpt) * 256 / 100;
int eg = get_option_value_int(egOpt) * 256 / 100;
// Convert to integer to prevent overflow return apply_weight(make_score(mg, eg), internalWeight);
int mg = mg_value(uciWeight);
int eg = eg_value(uciWeight);
mg = (mg * 0x100) / 100;
eg = (eg * 0x100) / 100;
mg = (mg * mg_value(internalWeight)) / 0x100;
eg = (eg * eg_value(internalWeight)) / 0x100;
return make_score(mg, eg);
} }
// init_safety() initizes the king safety evaluation, based on UCI // init_safety() initizes the king safety evaluation, based on UCI
@@ -1204,27 +1101,24 @@ namespace {
void init_safety() { void init_safety() {
int maxSlope = 30; const Value MaxSlope = Value(30);
int peak = 0x500; const Value Peak = Value(1280);
double a = 0.4; Value t[100];
double b = 0.0;
// First setup the base table
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
if (i < b) t[i] = Value(int(0.4 * i * i));
SafetyTable[i] = Value(0);
else if (i > 0)
SafetyTable[i] = Value((int)(a * (i - b) * (i - b))); t[i] = Min(t[i], t[i - 1] + MaxSlope);
t[i] = Min(t[i], Peak);
} }
// Then apply the weights and get the final KingDangerTable[] array
for (Color c = WHITE; c <= BLACK; c++)
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
if (SafetyTable[i+1] - SafetyTable[i] > maxSlope)
for (int j = i + 1; j < 100; j++)
SafetyTable[j] = SafetyTable[j-1] + Value(maxSlope);
if (SafetyTable[i] > Value(peak))
SafetyTable[i] = Value(peak);
}
} }
} }

View File

@@ -47,7 +47,7 @@ class Position;
struct EvalInfo { struct EvalInfo {
EvalInfo() { futilityMargin[0] = futilityMargin[1] = Value(0); } EvalInfo() { kingDanger[0] = kingDanger[1] = Value(0); }
// Middle game and endgame evaluations // Middle game and endgame evaluations
Score value; Score value;
@@ -89,15 +89,11 @@ struct EvalInfo {
// 2 to kingAdjacentZoneAttacksCount[BLACK]. // 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[2]; int kingAdjacentZoneAttacksCount[2];
// mateThreat[color] is a move for the given side which gives a direct mate. // Middle game and endgame mobility scores
Move mateThreat[2];
// Middle game and endgame mobility scores.
Score mobility; Score mobility;
// Extra futility margin. This is added to the standard futility margin // Value of the danger for the king of the given color
// in the quiescence search. One for each color. Value kingDanger[2];
Value futilityMargin[2];
}; };
@@ -105,7 +101,7 @@ struct EvalInfo {
//// Prototypes //// Prototypes
//// ////
extern Value evaluate(const Position& pos, EvalInfo& ei, int threadID); 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_weights(Color sideToMove);

View File

@@ -57,8 +57,19 @@ int main(int argc, char *argv[]) {
CALLGRIND_START_INSTRUMENTATION; CALLGRIND_START_INSTRUMENTATION;
#endif #endif
// Process command line arguments if any if (argc <= 1)
if (argc > 1) {
// Print copyright notice
cout << engine_name()
<< " by Tord Romstad, Marco Costalba, Joona Kiiski" << endl;
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT." << endl;
// Enter UCI mode
uci_main_loop();
}
else // Process command line arguments
{ {
if (string(argv[1]) != "bench" || argc < 4 || argc > 8) if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
cout << "Usage: stockfish bench <hash size> <threads> " cout << "Usage: stockfish bench <hash size> <threads> "
@@ -73,17 +84,8 @@ int main(int argc, char *argv[]) {
string tim = argc > 7 ? argv[7] : ""; string tim = argc > 7 ? argv[7] : "";
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim); benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim);
} }
return 0;
} }
// Print copyright notice Application::free_resources();
cout << engine_name()
<< ". By Tord Romstad, Marco Costalba, Joona Kiiski." << endl;
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT. We will use it." << endl;
// Enter UCI mode
uci_main_loop();
return 0; return 0;
} }

View File

@@ -55,17 +55,40 @@ namespace {
{ 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 } };
// Named endgame evaluation and scaling functions, these
// are accessed direcly and not through the function maps.
EvaluationFunction<KmmKm> EvaluateKmmKm(WHITE);
EvaluationFunction<KXK> EvaluateKXK(WHITE), EvaluateKKX(BLACK);
ScalingFunction<KBPsK> ScaleKBPsK(WHITE), ScaleKKBPs(BLACK);
ScalingFunction<KQKRPs> ScaleKQKRPs(WHITE), ScaleKRPsKQ(BLACK);
ScalingFunction<KPsK> ScaleKPsK(WHITE), ScaleKKPs(BLACK);
ScalingFunction<KPKP> ScaleKPKPw(WHITE), ScaleKPKPb(BLACK);
typedef EndgameEvaluationFunctionBase EF; typedef EndgameEvaluationFunctionBase EF;
typedef EndgameScalingFunctionBase SF; typedef EndgameScalingFunctionBase SF;
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
EvaluationFunction<KmmKm> EvaluateKmmKm[] = { EvaluationFunction<KmmKm>(WHITE), EvaluationFunction<KmmKm>(BLACK) };
EvaluationFunction<KXK> EvaluateKXK[] = { EvaluationFunction<KXK>(WHITE), EvaluationFunction<KXK>(BLACK) };
ScalingFunction<KBPsK> ScaleKBPsK[] = { ScalingFunction<KBPsK>(WHITE), ScalingFunction<KBPsK>(BLACK) };
ScalingFunction<KQKRPs> ScaleKQKRPs[] = { ScalingFunction<KQKRPs>(WHITE), ScalingFunction<KQKRPs>(BLACK) };
ScalingFunction<KPsK> ScaleKPsK[] = { ScalingFunction<KPsK>(WHITE), ScalingFunction<KPsK>(BLACK) };
ScalingFunction<KPKP> ScaleKPKP[] = { ScalingFunction<KPKP>(WHITE), ScalingFunction<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == Value(0)
&& pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame;
}
template<Color Us> bool is_KBPsK(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
}
template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0
&& pos.non_pawn_material(Us) == QueenValueMidgame
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
} }
@@ -181,18 +204,9 @@ 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 ( pos.non_pawn_material(BLACK) == Value(0) else if (is_KXK<WHITE>(pos) || is_KXK<BLACK>(pos))
&& pos.piece_count(BLACK, PAWN) == 0
&& pos.non_pawn_material(WHITE) >= RookValueMidgame)
{ {
mi->evaluationFunction = &EvaluateKXK; mi->evaluationFunction = is_KXK<WHITE>(pos) ? &EvaluateKXK[WHITE] : &EvaluateKXK[BLACK];
return mi;
}
else if ( pos.non_pawn_material(WHITE) == Value(0)
&& pos.piece_count(WHITE, PAWN) == 0
&& pos.non_pawn_material(BLACK) >= RookValueMidgame)
{
mi->evaluationFunction = &EvaluateKKX;
return mi; return mi;
} }
else if ( pos.pieces(PAWN) == EmptyBoardBB else if ( pos.pieces(PAWN) == EmptyBoardBB
@@ -207,7 +221,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{ {
mi->evaluationFunction = &EvaluateKmmKm; mi->evaluationFunction = &EvaluateKmmKm[WHITE];
return mi; return mi;
} }
} }
@@ -215,10 +229,8 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// OK, we didn't find any special evaluation function for the current // OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function? // material configuration. Is there a suitable scaling function?
// //
// The code below is rather messy, and it could easily get worse later, // We face problems when there are several conflicting applicable
// if we decide to add more special cases. We face problems when there // scaling functions and we need to decide which one to use.
// are several conflicting applicable scaling functions and we need to
// decide which one to use.
SF* sf; SF* sf;
if ((sf = funcs->get<SF>(key)) != NULL) if ((sf = funcs->get<SF>(key)) != NULL)
@@ -230,48 +242,36 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
// Generic scaling functions that refer to more then one material // Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones. // distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function. // Note that these ones don't return after setting the function.
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame if (is_KBPsK<WHITE>(pos))
&& pos.piece_count(WHITE, BISHOP) == 1 mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKBPsK;
if ( pos.non_pawn_material(BLACK) == BishopValueMidgame if (is_KBPsK<BLACK>(pos))
&& pos.piece_count(BLACK, BISHOP) == 1 mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKKBPs;
if ( pos.piece_count(WHITE, PAWN) == 0 if (is_KQKRPs<WHITE>(pos))
&& pos.non_pawn_material(WHITE) == QueenValueMidgame mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
&& pos.piece_count(WHITE, QUEEN) == 1
&& pos.piece_count(BLACK, ROOK) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKQKRPs;
else if ( pos.piece_count(BLACK, PAWN) == 0 else if (is_KQKRPs<BLACK>(pos))
&& pos.non_pawn_material(BLACK) == QueenValueMidgame mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
&& pos.piece_count(BLACK, QUEEN) == 1
&& pos.piece_count(WHITE, ROOK) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKRPsKQ;
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(0))
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (pos.piece_count(BLACK, PAWN) == 0)
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK; mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (pos.piece_count(WHITE, PAWN) == 0)
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKKPs; mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKPw; mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
mi->scalingFunction[BLACK] = &ScaleKPKPb; mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
} }
} }
@@ -403,7 +403,7 @@ Key EndgameFunctions::buildKey(const string& keyCode) {
s << char(upcase? toupper(keyCode[i]) : tolower(keyCode[i])); s << char(upcase? toupper(keyCode[i]) : tolower(keyCode[i]));
} }
s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w -"; s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w -";
return Position(s.str()).get_material_key(); return Position(s.str(), 0).get_material_key();
} }
const string EndgameFunctions::swapColors(const string& keyCode) { const string EndgameFunctions::swapColors(const string& keyCode) {

View File

@@ -41,93 +41,110 @@
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/ */
#include "types.h"
#include "mersenne.h" #include "mersenne.h"
/* Period parameters */ namespace {
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
static unsigned long mt[N]; /* the array for the state vector */ // Period parameters
static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ 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) {
/* initializes mt[N] with a seed */
static void init_genrand(unsigned long s)
{
mt[0]= s & 0xffffffffUL; mt[0]= s & 0xffffffffUL;
for (mti=1; mti<N; mti++) { for (mti = 1; mti < N; mti++)
mt[mti] = {
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ // In the previous versions, MSBs of the seed affect
/* In the previous versions, MSBs of the seed affect */ // only MSBs of the array mt[].
/* only MSBs of the array mt[]. */ // 2002/01/09 modified by Makoto Matsumoto
/* 2002/01/09 modified by Makoto Matsumoto */ mt[mti] = 1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti;
mt[mti] &= 0xffffffffUL; mt[mti] &= 0xffffffffUL; // For > 32 bit machines
/* for >32 bit machines */ }
} }
}
/* initialize by an array with array-length */ // Initialize by an array with array-length
/* init_key is the array for initializing keys */ // init_key is the array for initializing keys
/* key_length is its length */ // key_length is its length
/* slight change for C++, 2004/2/26 */ // slight change for C++, 2004/2/26
static void init_by_array(unsigned long init_key[], int key_length) void init_by_array(unsigned long init_key[], int key_length) {
{
int i, j, k; int i = 1;
int j = 0;
int k = (N > key_length ? N : key_length);
init_genrand(19650218UL); init_genrand(19650218UL);
i=1; j=0;
k = (N>key_length ? N : key_length); for ( ; k; k--)
for (; k; k--) { {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; // Non linear
+ init_key[j] + j; /* non linear */ mt[i] &= 0xffffffffUL; // For WORDSIZE > 32 machines
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++; j++; i++; j++;
if (i>=N) { mt[0] = mt[N-1]; i=1; } if (i >= N)
if (j>=key_length) j=0; {
mt[0] = mt[N-1];
i = 1;
} }
for (k=N-1; k; k--) { if (j >= key_length)
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) j = 0;
- i; /* non linear */ }
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 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++; i++;
if (i>=N) { mt[0] = mt[N-1]; i=1; } if (i >= N)
{
mt[0] = mt[N-1];
i = 1;
}
}
mt[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
} }
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ } // namespace
}
// Generates a random number on [0,0xffffffff]-interval
uint32_t genrand_int32() {
/* generates a random number on [0,0xffffffff]-interval */
uint32_t genrand_int32(void) {
unsigned long y; unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A}; static unsigned long mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */ /* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */ // Generate N words at one time
if (mti >= N)
{
int kk; int kk;
if (mti == N+1) /* if init_genrand() has not been called, */ if (mti == N+1) // If init_genrand() has not been called,
init_genrand(5489UL); /* a default initial seed is used */ init_genrand(5489UL); // a default initial seed is used.
for (kk=0;kk<N-M;kk++) { for (kk = 0; kk < N-M; kk++)
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); {
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
} }
for (;kk<N-1;kk++) { for ( ; kk < N-1; kk++)
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); {
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
} }
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0; mti = 0;
} }
y = mt[mti++]; y = mt[mti++];
/* Tempering */ // Tempering
y ^= (y >> 11); y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL; y ^= (y << 15) & 0xefc60000UL;
@@ -136,14 +153,19 @@ uint32_t genrand_int32(void) {
return y; return y;
} }
uint64_t genrand_int64(void) { uint64_t genrand_int64() {
uint64_t x, y; uint64_t x, y;
x = genrand_int32(); y = genrand_int32(); x = genrand_int32();
return (x<<32)|y; y = genrand_int32();
return (x << 32) | y;
} }
void init_mersenne(void) { void init_mersenne() {
unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
unsigned long init[4] = {0x123, 0x234, 0x345, 0x456};
unsigned long length = 4;
init_by_array(init, length); init_by_array(init, length);
} }

View File

@@ -32,9 +32,9 @@
//// Prototypes //// Prototypes
//// ////
extern uint32_t genrand_int32(void); extern uint32_t genrand_int32();
extern uint64_t genrand_int64(void); extern uint64_t genrand_int64();
extern void init_mersenne(void); extern void init_mersenne();
#endif // !defined(MERSENNE_H_INCLUDED) #endif // !defined(MERSENNE_H_INCLUDED)

View File

@@ -39,6 +39,10 @@
#endif #endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <iomanip> #include <iomanip>
@@ -54,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.7.1"; static const string EngineVersion = "1.8 beta 2";
static const string AppName = "Stockfish"; static const string AppName = "Stockfish";
static const string AppTag = ""; static const string AppTag = "";
@@ -147,7 +151,7 @@ const string engine_name() {
const string cpu64(CpuHas64BitPath ? " 64bit" : ""); const string cpu64(CpuHas64BitPath ? " 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" string date(__DATE__); // From compiler, format is "Sep 21 2008"
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
@@ -287,4 +291,27 @@ int Bioskey()
return 0; return 0;
} }
} }
#endif #endif
/// 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
/// loaded from RAM, that can be very slow.
#if defined(NO_PREFETCH)
void prefetch(char*) {}
#else
void prefetch(char* addr) {
#if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
}
#endif

View File

@@ -55,6 +55,7 @@ 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 Bioskey();
extern void prefetch(char* addr);
//// ////

View File

@@ -101,11 +101,11 @@ inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
// Split positives vs non-positives // Split positives vs non-positives
do { do {
while ((++p)->score > 0); while ((++p)->score > 0) {}
if (p != d) if (p != d)
{ {
while (--d != p && d->score <= 0); while (--d != p && d->score <= 0) {}
tmp = *p; tmp = *p;
*p = *d; *p = *d;

View File

@@ -338,6 +338,10 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m))) ||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m)))
return false; return false;
// The promotion piece, if any, must be valid
if (move_promotion_piece(m) > QUEEN || move_promotion_piece(m) == PAWN)
return false;
// Proceed according to the square delta between the origin and // Proceed according to the square delta between the origin and
// destination squares. // destination squares.
switch (direction) switch (direction)

View File

@@ -70,18 +70,18 @@ namespace {
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
const History& h, SearchStack* ss, Value beta) : pos(p), H(h) { SearchStack* ss, Value beta) : pos(p), H(h) {
int searchTT = ttm; int searchTT = ttm;
ttMoves[0].move = ttm; ttMoves[0].move = ttm;
lastBadCapture = badCaptures;
badCaptureThreshold = 0; badCaptureThreshold = 0;
lastBadCapture = badCaptures;
pinned = p.pinned_pieces(pos.side_to_move()); pinned = p.pinned_pieces(pos.side_to_move());
if (ss && !p.is_check()) if (ss && !p.is_check())
{ {
ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller; ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move; searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0]; killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
@@ -98,7 +98,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
badCaptureThreshold = -PawnValueMidgame; badCaptureThreshold = -PawnValueMidgame;
phasePtr = MainSearchPhaseTable; phasePtr = MainSearchPhaseTable;
} else if (d == Depth(0)) }
else if (d == Depth(0))
phasePtr = QsearchWithChecksPhaseTable; phasePtr = QsearchWithChecksPhaseTable;
else else
{ {
@@ -111,7 +112,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
searchTT = ttMoves[0].move = MOVE_NONE; searchTT = ttMoves[0].move = MOVE_NONE;
} }
phasePtr += !searchTT - 1; phasePtr += int(!searchTT) - 1;
go_next_phase(); go_next_phase();
} }
@@ -312,9 +313,9 @@ Move MovePicker::get_next_move() {
case PH_KILLERS: case PH_KILLERS:
move = (curMove++)->move; move = (curMove++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned)
&& move != ttMoves[0].move && move != ttMoves[0].move
&& move != ttMoves[1].move && move != ttMoves[1].move
&& move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move)) && !pos.move_is_capture(move))
return move; return move;
break; break;

View File

@@ -61,11 +61,11 @@ private:
const Position& pos; const Position& pos;
const History& H; const History& H;
Bitboard pinned;
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, *lastBadCapture;
Bitboard pinned;
MoveStack moves[256], badCaptures[64]; MoveStack moves[256], badCaptures[64];
}; };

View File

@@ -94,12 +94,13 @@ namespace {
}; };
// Pawn storm open file bonuses by file // Pawn storm open file bonuses by file
const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 }; const int16_t QStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 }; const int16_t KStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
// Pawn storm lever bonuses by file // Pawn storm lever bonuses by file
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 }; const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
#undef S
} }
@@ -107,11 +108,10 @@ namespace {
//// Functions //// Functions
//// ////
/// Constructor /// PawnInfoTable c'tor and d'tor instantiated one each thread
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) { PawnInfoTable::PawnInfoTable(unsigned numOfEntries) : size(numOfEntries) {
size = numOfEntries;
entries = new PawnInfo[size]; entries = new PawnInfo[size];
if (!entries) if (!entries)
{ {
@@ -122,9 +122,8 @@ PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
} }
/// Destructor
PawnInfoTable::~PawnInfoTable() { PawnInfoTable::~PawnInfoTable() {
delete [] entries; delete [] entries;
} }
@@ -140,11 +139,11 @@ void PawnInfo::clear() {
/// 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 /// a PawnInfo object, and returns a pointer to it. The result is also stored
/// stored in a hash table, so we don't have to recompute everything when /// in a hash table, so we don't have to recompute everything when the same
/// the same pawn structure occurs again. /// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) { PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
assert(pos.is_ok()); assert(pos.is_ok());
@@ -179,12 +178,13 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
template<Color Us> template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) { Bitboard theirPawns, PawnInfo* pi) const {
Bitboard b;
Square s; Square s;
File f; File f;
Rank r; Rank r;
bool passed, isolated, doubled, chain, backward, candidate;
int bonus; int bonus;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = make_score(0, 0); Score value = make_score(0, 0);
const Square* ptr = pos.piece_list_begin(Us, PAWN); const Square* ptr = pos.piece_list_begin(Us, PAWN);
@@ -205,69 +205,24 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN)); assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
// Passed, isolated or doubled pawn? // Calculate kingside and queenside pawn storm scores for both colors to be
passed = Position::pawn_is_passed(theirPawns, Us, s); // used when evaluating middle game positions with opposite side castling.
isolated = Position::pawn_is_isolated(ourPawns, s); bonus = (f >= FILE_F ? evaluate_pawn_storm<Us, KingSide>(s, r, f, theirPawns) : 0);
doubled = Position::pawn_is_doubled(ourPawns, Us, s); pi->ksStormValue[Us] += KStormTable[relative_square(Us, s)] + bonus;
// We calculate kingside and queenside pawn storm bonus = (f <= FILE_C ? evaluate_pawn_storm<Us, QueenSide>(s, r, f, theirPawns) : 0);
// scores for both colors. These are used when evaluating pi->qsStormValue[Us] += QStormTable[relative_square(Us, s)] + bonus;
// middle game positions with opposite side castling.
//
// Each pawn is given a base score given by a piece square table
// (KStormTable[] or QStormTable[]). Pawns which seem to have good
// chances of creating an open file by exchanging itself against an
// enemy pawn on an adjacent file gets an additional bonus.
// Kingside pawn storms // Our rank plus previous one. Used for chain detection.
bonus = KStormTable[relative_square(Us, s)]; b = rank_bb(r) | rank_bb(r + (Us == WHITE ? -1 : 1));
if (f >= FILE_F)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
while (b)
{
// Give a bonus according to the distance of the nearest enemy pawn
Square s2 = pop_1st_bit(&b);
int v = StormLeverBonus[f] - 2 * square_distance(s, s2);
// If enemy pawn has no pawn beside itself is particularly vulnerable. // Passed, isolated, doubled or member of a pawn
// Big bonus, especially against a weakness on the rook file // chain (but not the backward one) ?
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2))) passed = !(theirPawns & passed_pawn_mask(Us, s));
v *= (square_file(s2) == FILE_H ? 4 : 2); doubled = ourPawns & squares_behind(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s);
bonus += v; isolated = !(ourPawns & neighboring_files_bb(f));
} chain = ourPawns & neighboring_files_bb(f) & b;
}
pi->ksStormValue[Us] += bonus;
// Queenside pawn storms
bonus = QStormTable[relative_square(Us, s)];
if (f <= FILE_C)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
while (b)
{
// Give a bonus according to the distance of the nearest enemy pawn
Square s2 = pop_1st_bit(&b);
int v = StormLeverBonus[f] - 4 * square_distance(s, s2);
// 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) == FILE_A ? 4 : 2);
bonus += v;
}
}
pi->qsStormValue[Us] += bonus;
// Member of a pawn chain (but not the backward one)? We could speed up
// the test a little by introducing an array of masks indexed by color
// and square for doing the test, but because everything is hashed,
// it probably won't make any noticable difference.
chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1)));
// Test for backward pawn // Test for backward pawn
// //
@@ -276,7 +231,7 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// 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 & behind_bb(Us, r) & neighboring_files_bb(f)) || (ourPawns & attack_span_mask(opposite_color(Us), s))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false; backward = false;
else else
@@ -285,7 +240,7 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// pawn on neighboring files. We now check whether the pawn is // pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring // backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first. // files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b = pos.attacks_from<PAWN>(s, Us); b = pos.attacks_from<PAWN>(s, Us);
// Note that we are sure to find something because pawn is not passed // Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't. // nor isolated, so loop is potentially infinite, but it isn't.
@@ -297,12 +252,12 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
} }
assert(passed | opposed | (attack_span_mask(Us, s) & theirPawns));
// Test for candidate passed pawn // Test for candidate passed pawn
candidate = !passed candidate = !(opposed | passed)
&& !(theirPawns & file_bb(f)) && (b = attack_span_mask(opposite_color(Us), s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns) && count_1s_max_15(b) >= count_1s_max_15(attack_span_mask(Us, s) & theirPawns);
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns)
>= 0);
// In order to prevent doubled passed pawns from receiving a too big // In order to prevent doubled passed pawns from receiving a too big
// bonus, only the frontmost passed pawn on each file is considered as // bonus, only the frontmost passed pawn on each file is considered as
@@ -310,10 +265,12 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
if (passed && (ourPawns & squares_in_front_of(Us, s))) if (passed && (ourPawns & squares_in_front_of(Us, s)))
passed = false; passed = false;
// Score this pawn // Mark the pawn as passed. Pawn will be properly scored in evaluation
// because we need full attack info to evaluate passed pawns.
if (passed) if (passed)
set_bit(&(pi->passedPawns), s); set_bit(&(pi->passedPawns), s);
// Score this pawn
if (isolated) if (isolated)
{ {
value -= IsolatedPawnPenalty[f]; value -= IsolatedPawnPenalty[f];
@@ -340,13 +297,47 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
} }
/// 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 /// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total get_king_shelter() calls. /// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) { int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
unsigned shelter = 0;
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq); Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned shelter = 0;
unsigned r = ksq & (7 << 3); unsigned r = ksq & (7 << 3);
for (int i = 1, k = (c ? -8 : 8); i < 4; i++) for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
{ {
r += k; r += k;

View File

@@ -64,10 +64,10 @@ private:
Key key; Key key;
Bitboard passedPawns; Bitboard passedPawns;
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[2];
Square kingSquares[2];
Score value; Score value;
int16_t ksStormValue[2], qsStormValue[2]; int16_t ksStormValue[2], qsStormValue[2];
uint8_t halfOpenFiles[2]; uint8_t halfOpenFiles[2];
Square kingSquares[2];
uint8_t kingShelters[2]; uint8_t kingShelters[2];
}; };
@@ -78,14 +78,19 @@ private:
class PawnInfoTable { class PawnInfoTable {
enum SideType { KingSide, QueenSide };
public: public:
PawnInfoTable(unsigned numOfEntries); PawnInfoTable(unsigned numOfEntries);
~PawnInfoTable(); ~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos); PawnInfo* get_pawn_info(const Position& pos) const;
private: private:
template<Color Us> template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi); 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; unsigned size;
PawnInfo* entries; PawnInfo* entries;

View File

@@ -38,7 +38,7 @@ static const string PieceChars(" pnbrqk PNBRQK");
char piece_type_to_char(PieceType pt, bool upcase) { char piece_type_to_char(PieceType pt, bool upcase) {
return PieceChars[pt + upcase * 7]; return PieceChars[pt + int(upcase) * 7];
} }
PieceType piece_type_from_char(char c) { PieceType piece_type_from_char(char c) {

View File

@@ -47,7 +47,6 @@ using std::string;
Key Position::zobrist[2][8][64]; Key Position::zobrist[2][8][64];
Key Position::zobEp[64]; Key Position::zobEp[64];
Key Position::zobCastle[16]; Key Position::zobCastle[16];
Key Position::zobMaterial[2][8][16];
Key Position::zobSideToMove; Key Position::zobSideToMove;
Key Position::zobExclusion; Key Position::zobExclusion;
@@ -75,23 +74,23 @@ CheckInfo::CheckInfo(const Position& pos) {
} }
/// Position c'tors. Here we always create a slower but safer copy of /// Position c'tors. Here we always create a copy of the original position
/// the original position or the FEN string, we want the new born Position /// or the FEN string, we want the new born Position object do not depend
/// object do not depend on any external data. Instead if we know what we /// on any external data so we detach state pointer from the source one.
/// are doing and we need speed we can create a position with default
/// c'tor Position() and then use just fast_copy().
Position::Position() {} Position::Position(int th) : threadID(th) {}
Position::Position(const Position& pos) { Position::Position(const Position& pos, int th) {
memcpy(this, &pos, sizeof(Position)); memcpy(this, &pos, sizeof(Position));
detach(); // Always detach() in copy c'tor to avoid surprises detach(); // Always detach() in copy c'tor to avoid surprises
threadID = th;
} }
Position::Position(const string& fen) { Position::Position(const string& fen, int th) {
from_fen(fen); from_fen(fen);
threadID = th;
} }
@@ -341,7 +340,7 @@ void Position::print(Move m) const {
std::cout << std::endl; std::cout << std::endl;
if (m != MOVE_NONE) if (m != MOVE_NONE)
{ {
Position p(*this); Position p(*this, thread());
string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : ""); string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
std::cout << "Move is: " << col << move_to_san(p, m) << std::endl; std::cout << "Move is: " << col << move_to_san(p, m) << std::endl;
} }
@@ -697,14 +696,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(is_ok()); assert(is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
Bitboard key = st->key; Key key = st->key;
// Copy some fields of old state to our new StateInfo object except the // Copy some fields of old state to our new StateInfo object except the
// ones which are recalculated from scratch anyway, then switch our state // ones which are recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state. // pointer to point to the new, ready to be updated, state.
struct ReducedStateInfo { struct ReducedStateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
int castleRights, rule50, pliesFromNull; int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare; Square epSquare;
Score value; Score value;
Value npMaterial[2]; Value npMaterial[2];
@@ -716,8 +715,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Save the current key to the history[] array, in order to be able to // Save the current key to the history[] array, in order to be able to
// detect repetition draws. // detect repetition draws.
history[gamePly] = key; history[st->gamePly++] = key;
gamePly++;
// Update side to move // Update side to move
key ^= zobSideToMove; key ^= zobSideToMove;
@@ -774,7 +772,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
} }
// Prefetch TT access as soon as we know key is updated // Prefetch TT access as soon as we know key is updated
TT.prefetch(key); prefetch((char*)TT.first_entry(key));
// Move the piece // Move the piece
Bitboard move_bb = make_move_bb(from, to); Bitboard move_bb = make_move_bb(from, to);
@@ -809,13 +807,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
key ^= zobEp[st->epSquare]; key ^= zobEp[st->epSquare];
} }
} }
}
// Update incremental scores
st->value += pst_delta(piece, from, to);
// Set capture piece
st->capture = capture;
if (pm) // promotion ? if (pm) // promotion ?
{ {
@@ -828,13 +819,13 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
set_bit(&(byTypeBB[promotion]), to); set_bit(&(byTypeBB[promotion]), to);
board[to] = piece_of_color_and_type(us, promotion); board[to] = piece_of_color_and_type(us, promotion);
// Update material key
st->materialKey ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]];
st->materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1];
// Update piece counts // Update piece counts
pieceCount[us][PAWN]--;
pieceCount[us][promotion]++; pieceCount[us][promotion]++;
pieceCount[us][PAWN]--;
// Update material key
st->materialKey ^= zobrist[us][PAWN][pieceCount[us][PAWN]];
st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]-1];
// Update piece lists, move the last pawn at index[to] position // Update piece lists, move the last pawn at index[to] position
// and shrink the list. Add a new promotion piece to the list. // and shrink the list. Add a new promotion piece to the list.
@@ -856,6 +847,13 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update material // Update material
st->npMaterial[us] += piece_value_midgame(promotion); st->npMaterial[us] += piece_value_midgame(promotion);
} }
}
// Update incremental scores
st->value += pst_delta(piece, from, to);
// Set capture piece
st->capture = capture;
// Update the key with the final value // Update the key with the final value
st->key = key; st->key = key;
@@ -896,12 +894,16 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
/// Position::do_capture_move() is a private method used to update captured /// Position::do_capture_move() is a private method used to update captured
/// piece info. It is called from the main Position::do_move function. /// piece info. It is called from the main Position::do_move function.
void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep) { void Position::do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep) {
assert(capture != KING); assert(capture != KING);
Square capsq = to; Square capsq = to;
// If the captured piece was a pawn, update pawn hash key,
// otherwise update non-pawn material.
if (capture == PAWN)
{
if (ep) // en passant ? if (ep) // en passant ?
{ {
capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S); capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S);
@@ -913,6 +915,10 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ
board[capsq] = EMPTY; board[capsq] = EMPTY;
} }
st->pawnKey ^= zobrist[them][PAWN][capsq];
}
else
st->npMaterial[them] -= piece_value_midgame(capture);
// Remove captured piece // Remove captured piece
clear_bit(&(byColorBB[them]), capsq); clear_bit(&(byColorBB[them]), capsq);
@@ -925,19 +931,12 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ
// Update incremental scores // Update incremental scores
st->value -= pst(them, capture, capsq); st->value -= pst(them, capture, capsq);
// If the captured piece was a pawn, update pawn hash key,
// otherwise update non-pawn material.
if (capture == PAWN)
st->pawnKey ^= zobrist[them][PAWN][capsq];
else
st->npMaterial[them] -= piece_value_midgame(capture);
// Update material hash key
st->materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
// Update piece count // Update piece count
pieceCount[them][capture]--; pieceCount[them][capture]--;
// Update material hash key
st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]];
// Update piece list, move the last piece at index[capsq] position // Update piece list, move the last piece at index[capsq] position
// //
// WARNING: This is a not perfectly revresible operation. When we // WARNING: This is a not perfectly revresible operation. When we
@@ -1061,7 +1060,6 @@ void Position::undo_move(Move m) {
assert(is_ok()); assert(is_ok());
assert(move_is_ok(m)); assert(move_is_ok(m));
gamePly--;
sideToMove = opposite_color(sideToMove); sideToMove = opposite_color(sideToMove);
if (move_is_castle(m)) if (move_is_castle(m))
@@ -1111,7 +1109,6 @@ void Position::undo_move(Move m) {
pieceList[us][PAWN][index[to]] = to; pieceList[us][PAWN][index[to]] = to;
} }
// Put the piece back at the source square // Put the piece back at the source square
Bitboard move_bb = make_move_bb(to, from); Bitboard move_bb = make_move_bb(to, from);
do_move_bb(&(byColorBB[us]), move_bb); do_move_bb(&(byColorBB[us]), move_bb);
@@ -1246,21 +1243,20 @@ void Position::do_null_move(StateInfo& backupSt) {
// Save the current key to the history[] array, in order to be able to // Save the current key to the history[] array, in order to be able to
// detect repetition draws. // detect repetition draws.
history[gamePly] = st->key; history[st->gamePly++] = st->key;
// Update the necessary information // Update the necessary information
if (st->epSquare != SQ_NONE) if (st->epSquare != SQ_NONE)
st->key ^= zobEp[st->epSquare]; st->key ^= zobEp[st->epSquare];
st->key ^= zobSideToMove; st->key ^= zobSideToMove;
TT.prefetch(st->key); prefetch((char*)TT.first_entry(st->key));
sideToMove = opposite_color(sideToMove); sideToMove = opposite_color(sideToMove);
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
st->rule50++; st->rule50++;
st->pliesFromNull = 0; st->pliesFromNull = 0;
st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue; st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue;
gamePly++;
} }
@@ -1282,7 +1278,7 @@ void Position::undo_null_move() {
// Update the necessary information // Update the necessary information
sideToMove = opposite_color(sideToMove); sideToMove = opposite_color(sideToMove);
st->rule50--; st->rule50--;
gamePly--; st->gamePly--;
} }
@@ -1311,10 +1307,10 @@ int Position::see_sign(Move m) const {
Square from = move_from(m); Square from = move_from(m);
Square to = move_to(m); Square to = move_to(m);
// Early return if SEE cannot be negative because capturing piece value // Early return if SEE cannot be negative because captured piece value
// is not bigger then captured one. // is not less then capturing one. Note that king moves always return
if ( midgame_value_of_piece_on(from) <= midgame_value_of_piece_on(to) // here because king midgame value is set to 0.
&& type_of_piece_on(from) != KING) if (midgame_value_of_piece_on(to) >= midgame_value_of_piece_on(from))
return 1; return 1;
return see(from, to); return see(from, to);
@@ -1479,7 +1475,6 @@ void Position::clear() {
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
sideToMove = WHITE; sideToMove = WHITE;
gamePly = 0;
initialKFile = FILE_E; initialKFile = FILE_E;
initialKRFile = FILE_H; initialKRFile = FILE_H;
initialQRFile = FILE_A; initialQRFile = FILE_A;
@@ -1494,7 +1489,7 @@ void Position::clear() {
void Position::reset_game_ply() { void Position::reset_game_ply() {
gamePly = 0; st->gamePly = 0;
} }
@@ -1598,8 +1593,8 @@ Key Position::compute_material_key() const {
for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (PieceType pt = PAWN; pt <= QUEEN; pt++)
{ {
int count = piece_count(c, pt); int count = piece_count(c, pt);
for (int i = 0; i <= count; i++) for (int i = 0; i < count; i++)
result ^= zobMaterial[c][pt][i]; result ^= zobrist[c][pt][i];
} }
return result; return result;
} }
@@ -1672,8 +1667,8 @@ bool Position::is_draw() const {
return true; return true;
// Draw by repetition? // Draw by repetition?
for (int i = 4; i <= Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2) for (int i = 4, e = Min(Min(st->gamePly, st->rule50), st->pliesFromNull); i <= e; i += 2)
if (history[gamePly - i] == st->key) if (history[st->gamePly - i] == st->key)
return true; return true;
return false; return false;
@@ -1752,15 +1747,6 @@ void Position::init_zobrist() {
zobCastle[i] = genrand_int64(); zobCastle[i] = genrand_int64();
zobSideToMove = genrand_int64(); zobSideToMove = genrand_int64();
for (int i = 0; i < 2; i++)
for (int j = 0; j < 8; j++)
for (int k = 0; k < 16; k++)
zobMaterial[i][j][k] = (k > 0)? Key(genrand_int64()) : Key(0LL);
for (int i = 0; i < 16; i++)
zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
zobExclusion = genrand_int64(); zobExclusion = genrand_int64();
} }
@@ -1797,6 +1783,7 @@ void Position::flipped_copy(const Position& pos) {
assert(pos.is_ok()); assert(pos.is_ok());
clear(); clear();
threadID = pos.thread();
// Board // Board
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)

View File

@@ -70,9 +70,9 @@ struct CheckInfo {
CheckInfo(const Position&); CheckInfo(const Position&);
Square ksq;
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard checkSq[8];
Square ksq;
}; };
/// Castle rights, encoded as bit fields /// Castle rights, encoded as bit fields
@@ -100,13 +100,13 @@ enum Phase {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
int castleRights, rule50, pliesFromNull; int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare; Square epSquare;
Score value; Score value;
Value npMaterial[2]; Value npMaterial[2];
Key key;
PieceType capture; PieceType capture;
Key key;
Bitboard checkersBB; Bitboard checkersBB;
StateInfo* previous; StateInfo* previous;
}; };
@@ -139,6 +139,9 @@ class Position {
friend class MaterialInfo; friend class MaterialInfo;
friend class EndgameFunctions; friend class EndgameFunctions;
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
public: public:
enum GamePhase { enum GamePhase {
MidGame, MidGame,
@@ -146,9 +149,9 @@ public:
}; };
// Constructors // Constructors
Position(); explicit Position(int threadID);
explicit Position(const Position& pos); Position(const Position& pos, int threadID);
explicit Position(const std::string& fen); Position(const std::string& fen, int threadID);
// Text input/output // Text input/output
void from_fen(const std::string& fen); void from_fen(const std::string& fen);
@@ -228,9 +231,6 @@ public:
// Information about pawns // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
static bool pawn_is_passed(Bitboard theirPawns, Color c, Square s);
static bool pawn_is_isolated(Bitboard ourPawns, Square s);
static bool pawn_is_doubled(Bitboard ourPawns, Color c, Square s);
// Weak squares // Weak squares
bool square_is_weak(Square s, Color c) const; bool square_is_weak(Square s, Color c) const;
@@ -274,6 +274,9 @@ public:
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;
// Current thread ID searching on the position
int thread() const;
// Reset the gamePly variable to 0 // Reset the gamePly variable to 0
void reset_game_ply(); void reset_game_ply();
@@ -293,7 +296,7 @@ private:
void allow_ooo(Color c); void allow_ooo(Color c);
// Helper functions for doing and undoing moves // Helper functions for doing and undoing moves
void do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep); void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep);
void do_castle_move(Move m); void do_castle_move(Move m);
void undo_castle_move(Move m); void undo_castle_move(Move m);
void find_checkers(); void find_checkers();
@@ -326,18 +329,17 @@ private:
// Other info // Other info
Color sideToMove; Color sideToMove;
int gamePly;
Key history[MaxGameLength]; Key history[MaxGameLength];
int castleRightsMask[64]; int castleRightsMask[64];
File initialKFile, initialKRFile, initialQRFile;
StateInfo startState; StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
int threadID;
StateInfo* st; StateInfo* st;
// Static variables // Static variables
static Key zobrist[2][8][64]; static Key zobrist[2][8][64];
static Key zobEp[64]; static Key zobEp[64];
static Key zobCastle[16]; static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove; static Key zobSideToMove;
static Score PieceSquareTable[16][64]; static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
@@ -412,8 +414,8 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt]; return pieceCount[c][pt];
} }
inline Square Position::piece_list(Color c, PieceType pt, int index) const { inline Square Position::piece_list(Color c, PieceType pt, int idx) const {
return pieceList[c][pt][index]; return pieceList[c][pt][idx];
} }
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const { inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
@@ -485,20 +487,8 @@ inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s)); return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
} }
inline bool Position::pawn_is_passed(Bitboard theirPawns, Color c, Square s) {
return !(theirPawns & passed_pawn_mask(c, s));
}
inline bool Position::pawn_is_isolated(Bitboard ourPawns, Square s) {
return !(ourPawns & neighboring_files_bb(s));
}
inline bool Position::pawn_is_doubled(Bitboard ourPawns, Color c, Square s) {
return ourPawns & squares_behind(c, s);
}
inline bool Position::square_is_weak(Square s, Color c) const { inline bool Position::square_is_weak(Square s, Color c) const {
return !(pieces(PAWN, c) & outpost_mask(opposite_color(c), s)); return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
} }
inline Key Position::get_key() const { inline Key Position::get_key() const {
@@ -573,4 +563,8 @@ inline PieceType Position::captured_piece() const {
return st->capture; return st->capture;
} }
inline int Position::thread() const {
return threadID;
}
#endif // !defined(POSITION_H_INCLUDED) #endif // !defined(POSITION_H_INCLUDED)

View File

@@ -299,7 +299,7 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool
string moveStr; string moveStr;
size_t length = 0; size_t length = 0;
size_t maxLength = 80 - startColumn; size_t maxLength = 80 - startColumn;
Position p(pos); Position p(pos, pos.thread());
for (int i = 0; line[i] != MOVE_NONE; i++) for (int i = 0; line[i] != MOVE_NONE; i++)
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -54,11 +54,13 @@ struct SearchStack {
Move currentMove; Move currentMove;
Move mateKiller; Move mateKiller;
Move threatMove; Move threatMove;
Move excludedMove;
Move killers[KILLER_MAX]; Move killers[KILLER_MAX];
Depth reduction; Depth reduction;
Value eval; Value eval;
bool skipNullMove;
void init(int ply); void init();
void initKillers(); void initKillers();
}; };

View File

@@ -51,11 +51,10 @@ struct SplitPoint {
// Const data after splitPoint has been setup // Const data after splitPoint has been setup
SplitPoint* parent; SplitPoint* parent;
const Position* pos; const Position* pos;
bool pvNode;
Depth depth; Depth depth;
bool mateThreat; bool pvNode, mateThreat;
Value beta; Value beta;
int ply, master, slaves[MAX_THREADS]; int ply;
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
@@ -66,9 +65,9 @@ struct SplitPoint {
Lock lock; Lock lock;
volatile Value alpha; volatile Value alpha;
volatile Value bestValue; volatile Value bestValue;
volatile int moves; volatile int moveCount;
volatile int cpus;
volatile bool stopRequest; volatile bool stopRequest;
volatile int slaves[MAX_THREADS];
}; };
// ThreadState type is used to represent thread's current state // ThreadState type is used to represent thread's current state
@@ -84,7 +83,7 @@ enum ThreadState
}; };
struct Thread { struct Thread {
SplitPoint* splitPoint; SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;
uint64_t nodes; uint64_t nodes;
uint64_t betaCutOffs[2]; uint64_t betaCutOffs[2];

View File

@@ -25,9 +25,6 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#if !(defined(__hpux) || defined(__ppc__) || defined(__ppc64__) || defined(__arm__))
# include <xmmintrin.h>
#endif
#include "movegen.h" #include "movegen.h"
#include "tt.h" #include "tt.h"
@@ -91,16 +88,6 @@ void TranspositionTable::clear() {
} }
/// TranspositionTable::first_entry returns a pointer to the first
/// entry of a cluster given a position. The low 32 bits of the key
/// are used to get the index in the table.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[uint32_t(posKey) & (size - 1)].data;
}
/// TranspositionTable::store writes a new entry containing a position, /// TranspositionTable::store writes a new entry containing a position,
/// a value, a value type, a search depth, and a best move to the /// a value, a value type, a search depth, and a best move to the
/// transposition table. Transposition table is organized in clusters of /// transposition table. Transposition table is organized in clusters of
@@ -111,7 +98,7 @@ inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
/// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL /// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL
/// never replaces another entry for the same position. /// never replaces another entry for the same position.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m) { void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) {
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
@@ -121,15 +108,11 @@ 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
{ {
// Do not overwrite when new type is VALUE_TYPE_EV_LO
if (tte->key() && t == VALUE_TYPE_EV_LO)
return;
// Preserve any exsisting ttMove // Preserve any exsisting ttMove
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = tte->move(); m = tte->move();
*tte = TTEntry(posKey32, v, t, d, m, generation); 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 else if (i == 0) // replace would be a no-op in this common case
@@ -142,7 +125,7 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
*replace = TTEntry(posKey32, v, t, d, m, generation); replace->save(posKey32, v, t, d, m, generation, statV, kingD);
writes++; writes++;
} }
@@ -164,31 +147,6 @@ TTEntry* TranspositionTable::retrieve(const Key posKey) const {
} }
/// TranspositionTable::prefetch looks up the current position in the
/// transposition table and load it in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data
/// to be loaded from RAM, that can be very slow. When we will
/// subsequently call retrieve() the TT data will be already
/// quickly accessible in L1/L2 CPU cache.
#if defined(__hpux) || defined(__ppc__) || defined(__ppc64__) || defined(__arm__)
void TranspositionTable::prefetch(const Key) const {} // Not supported on HP UX
#else
void TranspositionTable::prefetch(const Key posKey) const {
#if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
char const* addr = (char*)first_entry(posKey);
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
}
#endif
/// TranspositionTable::new_search() is called at the beginning of every new /// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to /// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from /// distinguish transposition table entries from previous searches from
@@ -209,13 +167,13 @@ void TranspositionTable::new_search() {
void TranspositionTable::insert_pv(const Position& pos, Move pv[]) { void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
StateInfo st; StateInfo st;
Position p(pos); Position p(pos, pos.thread());
for (int i = 0; pv[i] != MOVE_NONE; i++) for (int i = 0; pv[i] != MOVE_NONE; i++)
{ {
TTEntry *tte = retrieve(p.get_key()); TTEntry *tte = retrieve(p.get_key());
if (!tte || tte->move() != pv[i]) if (!tte || tte->move() != pv[i])
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), 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); p.do_move(pv[i], st);
} }
} }
@@ -231,7 +189,7 @@ void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PL
const TTEntry* tte; const TTEntry* tte;
StateInfo st; StateInfo st;
Position p(pos); Position p(pos, pos.thread());
int ply = 0; int ply = 0;
// Update position to the end of current PV // Update position to the end of current PV

View File

@@ -46,43 +46,52 @@
/// 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-18: not used /// bit 17-19: not used
/// bit 19-22: value type /// bit 20-22: value type
/// bit 23-31: generation /// bit 23-31: generation
class TTEntry { class TTEntry {
public: public:
TTEntry() {} void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value kd) {
TTEntry(uint32_t k, Value v, ValueType t, Depth d, Move m, int generation)
: key_ (k), data((m & 0x1FFFF) | (t << 19) | (generation << 23)),
value_(int16_t(v)), depth_(int16_t(d)) {}
uint32_t key() const { return key_; } key32 = k;
Depth depth() const { return Depth(depth_); } data = (m & 0x1FFFF) | (t << 20) | (g << 23);
value16 = int16_t(v);
depth16 = int16_t(d);
staticValue = int16_t(statV);
kingDanger = int16_t(kd);
}
uint32_t key() const { return key32; }
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(value_); } Value value() const { return Value(value16); }
ValueType type() const { return ValueType((data >> 19) & 0xF); } ValueType type() const { return ValueType((data >> 20) & 7); }
int generation() const { return (data >> 23); } int generation() const { return data >> 23; }
Value static_value() const { return Value(staticValue); }
Value king_danger() const { return Value(kingDanger); }
private: private:
uint32_t key_; uint32_t key32;
uint32_t data; uint32_t data;
int16_t value_; int16_t value16;
int16_t depth_; int16_t depth16;
int16_t staticValue;
int16_t kingDanger;
}; };
/// This is the number of TTEntry slots for each position /// This is the number of TTEntry slots for each position
const int ClusterSize = 5; const int ClusterSize = 4;
/// Each group of ClusterSize number of TTEntry form a TTCluster /// Each group of ClusterSize number of TTEntry form a TTCluster
/// that is indexed by a single position key. Cluster is padded /// that is indexed by a single position key. TTCluster size must
/// to a cache line size so to guarantee always aligned accesses. /// be not bigger then a cache line size, in case it is less then
/// it should be padded to guarantee always aligned accesses.
struct TTCluster { struct TTCluster {
TTEntry data[ClusterSize]; TTEntry data[ClusterSize];
char cache_line_padding[64 - sizeof(TTEntry[ClusterSize])];
}; };
@@ -97,17 +106,15 @@ public:
~TranspositionTable(); ~TranspositionTable();
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m); 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 prefetch(const Key posKey) const;
void new_search(); void new_search();
void insert_pv(const Position& pos, Move pv[]); void insert_pv(const Position& pos, Move pv[]);
void extract_pv(const Position& pos, Move pv[], const int PLY_MAX); void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
int full() const; int full() const;
TTEntry* first_entry(const Key posKey) const;
private: private:
inline TTEntry* first_entry(const Key posKey) const;
// Be sure 'writes' is at least one cache line away // Be sure 'writes' is at least one cache line away
// from read only variables. // from read only variables.
unsigned char pad_before[64 - sizeof(unsigned)]; unsigned char pad_before[64 - sizeof(unsigned)];
@@ -121,4 +128,14 @@ private:
extern TranspositionTable TT; extern TranspositionTable TT;
/// TranspositionTable::first_entry returns a pointer to the first
/// entry of a cluster given a position. The low 32 bits of the key
/// are used to get the index in the table.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[uint32_t(posKey) & (size - 1)].data;
}
#endif // !defined(TT_H_INCLUDED) #endif // !defined(TT_H_INCLUDED)

View File

@@ -49,20 +49,32 @@ typedef uint64_t Bitboard;
//// ////
//// Compiler specific defines //// Configuration
//// ////
// Quiet a warning on Intel compiler //// For Linux and OSX configuration is done automatically using Makefile.
#if !defined(__SIZEOF_INT__ ) //// To get started type "make help".
#define __SIZEOF_INT__ 0 ////
#endif //// For windows part of the configuration is detected automatically, but
//// some switches need to be set manually:
////
//// -DNDEBUG | Disable debugging mode. Use always.
////
//// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want the
//// | executable to run on some very old machines.
////
//// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction.
//// | Works only in 64-bit mode. For compiling requires hardware
//// | with popcnt support. Around 4% speed-up.
// Check for 64 bits for different compilers: Intel, MSVC and gcc // Automatic detection for 64-bit under Windows
#if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4) #if defined(_WIN64)
#define IS_64BIT #define IS_64BIT
#endif #endif
#if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER)) // Automatic detection for use of bsfq asm-instruction under Windows.
// Works only in 64-bit mode. Does not work with MSVC.
#if defined(_WIN64) && defined(__INTEL_COMPILER)
#define USE_BSFQ #define USE_BSFQ
#endif #endif

View File

@@ -54,7 +54,7 @@ namespace {
// The root position. This is set up when the user (or in practice, the GUI) // 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() // sends the "position" UCI command. The root position is sent to the think()
// function when the program receives the "go" command. // function when the program receives the "go" command.
Position RootPosition; Position RootPosition(0);
// Local functions // Local functions
bool handle_command(const string& command); bool handle_command(const string& command);
@@ -143,7 +143,7 @@ namespace {
RootPosition.print(); RootPosition.print();
else if (token == "flip") else if (token == "flip")
{ {
Position p(RootPosition); Position p(RootPosition, RootPosition.thread());
RootPosition.flipped_copy(p); RootPosition.flipped_copy(p);
} }
else if (token == "eval") else if (token == "eval")
@@ -151,7 +151,7 @@ namespace {
EvalInfo ei; EvalInfo ei;
cout << "Incremental mg: " << mg_value(RootPosition.value()) cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value()) << "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl; << "\nFull eval: " << evaluate(RootPosition, ei) << endl;
} }
else if (token == "key") else if (token == "key")
cout << "key: " << hex << RootPosition.get_key() cout << "key: " << hex << RootPosition.get_key()
@@ -308,7 +308,7 @@ namespace {
string token; string token;
int depth, tm, n; int depth, tm, n;
Position pos(RootPosition); Position pos(RootPosition, RootPosition.thread());
if (!(uip >> depth)) if (!(uip >> depth))
return; return;

View File

@@ -79,6 +79,7 @@ namespace {
o["Use Search Log"] = Option(false); o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt"); o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin"); o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false);
o["Mobility (Middle Game)"] = Option(100, 0, 200); o["Mobility (Middle Game)"] = Option(100, 0, 200);
o["Mobility (Endgame)"] = Option(100, 0, 200); o["Mobility (Endgame)"] = Option(100, 0, 200);
o["Pawn Structure (Middle Game)"] = Option(100, 0, 200); o["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
@@ -92,8 +93,8 @@ namespace {
o["Check Extension (non-PV nodes)"] = Option(1, 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 (PV nodes)"] = Option(2, 0, 2);
o["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2); o["Single Evasion Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (PV nodes)"] = Option(0, 0, 2); o["Mate Threat Extension (PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (non-PV nodes)"] = Option(0, 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 (PV nodes)"] = Option(1, 0, 2);
o["Pawn Push to 7th Extension (non-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 (PV nodes)"] = Option(1, 0, 2);
@@ -113,9 +114,6 @@ namespace {
o["UCI_Chess960"] = Option(false); o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false); o["UCI_AnalyseMode"] = Option(false);
// Temporary hack for 1.7.1 to be removed in next release
o["Zugzwang detection"] = Option(false);
// Any option should know its name so to be easily printed // Any option should know its name so to be easily printed
for (Options::iterator it = o.begin(); it != o.end(); ++it) for (Options::iterator it = o.begin(); it != o.end(); ++it)
it->second.name = it->first; it->second.name = it->first;

View File

@@ -36,13 +36,7 @@ enum ValueType {
VALUE_TYPE_NONE = 0, VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1, // Upper bound VALUE_TYPE_UPPER = 1, // Upper bound
VALUE_TYPE_LOWER = 2, // Lower bound VALUE_TYPE_LOWER = 2, // Lower bound
VALUE_TYPE_EXACT = 3, // Exact score VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
VALUE_TYPE_EVAL = 4, // Static evaluation value
VALUE_TYPE_NULL = 8, // Null search value
VALUE_TYPE_EV_UP = VALUE_TYPE_EVAL | VALUE_TYPE_UPPER,
VALUE_TYPE_EV_LO = VALUE_TYPE_EVAL | VALUE_TYPE_LOWER,
VALUE_TYPE_NS_LO = VALUE_TYPE_NULL | VALUE_TYPE_LOWER,
}; };