Compare commits

..

139 Commits

Author SHA1 Message Date
Marco Costalba
bc0871acbc Stockfish 1.6.1
Workaround a gcc optimization bug.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-26 19:39:22 +01:00
Marco Costalba
2643f1552f Workaround optimization bug in gcc
Unfortunatly we need to slow down to -O1 to be sure
it works always.

Note that sometime it works also with -O2 or even -O3,
but user has to try himself.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-26 19:39:13 +01:00
Marco Costalba
ba07b95ee0 Fix description of Score enum
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-26 19:39:04 +01:00
Marco Costalba
ef58551a2d Fix a typo in ReducedStateInfo
It happened to work by accident because Score and
Value are both integer.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-26 19:38:53 +01:00
Marco Costalba
7d34e7bf84 Stockfish 1.6
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-22 22:11:10 +01:00
Marco Costalba
12aeac5e14 Score definition gives a compile error under gcc
For enum definitions a parenthesis is required.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-21 11:44:11 +01:00
Marco Costalba
734fb9a13b Setup Release Candidate 1
To be used by Jim for testing different compiles settings.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-19 17:00:33 +01:00
Marco Costalba
c12364bb67 Fix a comment in HistoryMax description
Was obsoleted out some time ago.

Spotted by Justin Blanchard

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-19 10:43:12 +01:00
Marco Costalba
ad5b5cef4a Fix book name is hard coded as book.bin
Instead should be read by the corresponding UCI
option "Book File".

Bug reported and fixed by Justin Blanchard (Arch Linux)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-18 10:11:50 +01:00
Marco Costalba
0d88b832e3 In non-PV IID don't call evaluate when in check
Was a long standing hidden bug from Glaurung times,
triggered only now that we enable IID at non PV nodes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-15 12:07:23 +01:00
Marco Costalba
14c3da5cad Fix a compile error in debug mode
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-15 11:52:57 +01:00
Marco Costalba
0b9b34655f Enable IID at non-PV nodes
We want to increrase the opportunities
of doing an exclusion search.

After 999 games at 1+0
Mod vs Orig +216 =574 -209 50.35%  503.0/999  +2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-14 11:44:37 +01:00
Joona Kiiski
a66f31f129 Synchronize pruning rules in search and sp_search
Regression test passed:

Mod - Orig: 365 - 351

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-13 10:03:46 +01:00
Marco Costalba
2161d8b0b3 Remove history counters
Instead decrement history value on failure.

After 999 games at 1+0

Mod vs Orig  +236 =558 -204 51.60% +11 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-12 19:23:10 +01:00
Joona Kiiski
7bc72d092f Fix overflow risk in split point
Sizeof of search stack should be PLY_MAX+2 instead of PLY_MAX.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-10 19:00:19 +01:00
Joona Kiiski
b056e5d40a Re-enable TT.insert_pv()
This time make sure that valuable TTentries are not overwritten.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-09 19:43:12 +01:00
Joona Kiiski
d0b8bc5fdf Disable insert_pv
This way we avoid overwriting valuable TT entries which
are needed to calculate exclusion search extension for pv.

Mod - Orig: 483 - 410 (+28 elo!)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-08 18:13:38 +01:00
Marco Costalba
9a46ac6b2c Set IncrementalFutilityMargin to 8
After 999 games we are almost equal (+2 ELO),
but we have a good result against Rybka

Rybka 2.3.2a mp 32-bit vs Mod  254.5 - 242.5 +152/-140/=205 51.21%
Rybka 2.3.2a mp 32-bit vs Orig 259.5 - 236.5 +151/-128/=217 52.32%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-08 11:16:14 +01:00
Joona Kiiski
0fc9d9ef61 Replace 100 with PLY_MAX in ok_to_use_TT
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-03 15:12:50 +01:00
Joona Kiiski
bd618941ce Adjust SingleReplyMargin 0x64 -> 0x20
Mod - Orig: 920 - 890 (+6 elo)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-02 19:49:50 +01:00
Marco Costalba
403db5a6e9 Don't clear hash at the beginning of a new game
After 900 games at 1+0
Mod vs Orig +217 =480 -196 +8 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-02 15:13:13 +01:00
Marco Costalba
3f3365221b Try to prune also when approximateEval < beta
Now we always try to filter out moves, we will have
more wasted evaluation calls, but also more pruned
nodes.

After 786 games

Mod vs Orig +196 =413 -177 +8 ELO

Verified also against Rybka it increases score to 50-51%

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-12-01 11:49:33 +01:00
Joona Kiiski
ae0b965711 Do not crash if we are asked to search mate or stalemate position.
We might be asked to ponder mate or stalemate position.
This being the case, simply wait for stop or ponderhit.
Currently we crash.

UCI specs aren't clear on the issue, but it cost nothing to
add little check.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-28 21:41:57 +01:00
Marco Costalba
455993b289 Fix get_option_value() for strings with spaces
Problem is that

istream& operator>> (istream& is, char* str );

according to C++ documentation "Ends extraction when the
next character is either a valid whitespace or a null character,
or if the End-Of-File is reached."

So if the parameter value is a string with spaces the currently
used instruction 'ss >> ret;' copies the chars only up to the first
white space and not the whole string.

Use a specialization of get_option_value() to fix this corner case.

Bug reported by xiaozhi

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-28 17:31:56 +01:00
Marco Costalba
c7a77dd3c0 Retire FutilityMargins[] array
Now we use a formula to calculate margins on the fly.

Node count has changed because we fixed a leftover when
we still where using FutilityMargins to calculate futilityValue
in the case that we had the evaluation score in TT.

Also small indentation fix.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-28 11:56:08 +01:00
Marco Costalba
af3dd21e90 IncrementalFutilityMargin to 4 and increased pruning
Increase pruning at low depths while tone downa bit at
higher depths (linearize a bit the logaritmic behaviour)

This goes togheter with IncrementalFutilityMargin decreased
to 4 compensate the bigger pruning effect.

Total pruned nodes are more or less the same. We go from 36%
of nodes after prune to 37% with this patch.

After 999 games at 1+0
Mod vs Orig +250 =526 -223 +9 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-27 21:17:34 +01:00
Joona Kiiski
1a7047f544 Drop OnlyMoveExt PV-condition from 8 plies to 6 plies
Orig - Mod: 731 - 750

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-27 15:56:26 +01:00
Marco Costalba
25c22ffe7a Use move not ttMove in exclude search
If we arrive until the exclusion search call then
we know that move == ttMove == tte->move()

But using ttMove in search call while, during excluded search
conditions we have used tte->Move()could be a little bit suboptimal.
On the other side using tte->move() also in search call is a bit ugly
so opt for the third choice that is the most clean becasue from the
conditions the reader easily understands that we are talking of ttMove
and that we ant to exclude the move we are evaluating in that moment.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-27 15:38:51 +01:00
Marco Costalba
ae6157fcf3 Better document previous patch
If tte->move() != MOVE_NONE then tte->move() == ttMove

What could happen is that we have a ttMove without a tte, or,
we have a tte but tte->move() == MOVE_NONE

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-27 11:54:07 +01:00
Marco Costalba
5bec768d42 Fix a possible crash in excluded search condition
Due to IID we could have a ttMove and not a tte, or,
even if we have a tte they could belong to different
searches so that the depth and type of tte don't
have the same origin of the ttMove.

To fix this we always use tte entry in excluded search
condition and, after an IID, we reprobe the TT table.

No functional change. Apart from possible crash fix.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-26 13:58:55 +01:00
Marco Costalba
6ae30e7cb1 Document why we don't use TT to prune in search_pv()
From a Joona' s post on talkchess.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 17:42:52 +01:00
Joona Kiiski
5ea8167921 Revert last Only move extensions tweaks
They gave bad results:

Mod - Orig: 361 - 404

Master is now verified to be functional equivalent with F_63

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 17:42:51 +01:00
Marco Costalba
55b5b03273 Speed up sorting of non-captures
Becasue we have a lot of zero scores (around 30% of moves)
it is a good idea to do a couple a presorting loops across
the move list and shuffle the moves a bit so that with a
small effort we end up with 3 groups of moves: positives
scores, zero scores and negative scores.

We have two advantages

1) We don't need to sort zero scores

2) Sort two small groups is faster then sort a single big one

Speed up is of about 2%

Because equal scored moves could be reordered in a different way
this is not a "no functional change" although I have verified
the output list is always correctly sorted.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 17:42:41 +01:00
Marco Costalba
850c021f86 Rewrite messy LSN-code take 2
We already reset loseOnTime flag at the beginning of
a new game, so we can simplify a bit the ligic there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 14:35:32 +01:00
Joona Kiiski
cd0c7373cd Rewrite messy LSN-code
* New version is documented and logic should be easier to follow
* Add extra check to not use LSN with x moves / y seconds time control
* New code fixes some rear cases where old code (still) causes program to lose on time at move 1.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 12:10:47 +01:00
Joona Kiiski
9a59535962 Remove RootMoveList::scan_for_easy_move()
* The function is called only in one place
* It must not be called elsewhere
* The function call easily replaced with simple one line condition

No functional change (tested with usual set + 2000 random positions)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 11:57:20 +01:00
Joona Kiiski
48246468f2 Remove 2 FIXMEs from search.cpp
* First one is without any documentation, code is working just fine,
  so there seems to be nothing that really should be fixed.

* Second one requesting emergency measures on aspiration fail low
  when we are running out of time and we are without good move.
  After very long time, I've come to conclusion that this is
  impossible to fix, so remove request.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-25 11:52:09 +01:00
Marco Costalba
2b6bc70f7b Document and cleanup new effective-single-reply code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:12:49 +01:00
Joona Kiiski
8b3fdec7ec Always extend full ply in PV
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:02:34 +01:00
Joona Kiiski
58452de86d Add mild extension in low depths
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:02:27 +01:00
Joona Kiiski
93c9f342ca Fix currentMove bug
Orig vs Master: +15 elo 887.5 - 812.5 (1700 games, finished) [4CPU]

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:01:05 +01:00
Joona Kiiski
16acf57773 Only move extension based on exclusion search
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:00:13 +01:00
Joona Kiiski
77eec9f9cb Base work for exclusion search
No functional change

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 21:00:05 +01:00
Marco Costalba
bc35f4c42d Tone down a bit futility parameters
After 999 games at 1+0

Mod vs Orig +239 =542 -218  +7 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:27 +01:00
Marco Costalba
889c8538a8 Remove 4*IncrementalFutilityMargin from futilityValue
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:26 +01:00
Marco Costalba
c52da3b806 Logaritmic futility margins
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:24 +01:00
Marco Costalba
b599da01fa Exponential futility margins
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:23 +01:00
Marco Costalba
52bca81dcb History pruning exponential limit
Use an exponenital law instead of a linear one for
history pruning.

This should prune more at low depths and a bit less
at high depths.

After 965 games

Mod vs Orig +233 =504 -228 +2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:21 +01:00
Marco Costalba
0eedf47661 Incremental Futility Margin
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-23 20:59:20 +01:00
Marco Costalba
989833205f In razor qsearch use corrected beta
Correct beta by razor margin when callin qsearch

After 1019 games on Joona's QUAD

Mod - Orig: 524 - 495 (+10 elo)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-22 21:15:13 +01:00
Marco Costalba
87507121d5 Code style triviality
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-22 21:12:33 +01:00
Marco Costalba
89fe8bc0a6 Micro-optimize get_material_info()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-15 09:35:22 +01:00
Marco Costalba
4c58db0dab Convert pawns evaluation to Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-14 17:57:50 +01:00
Marco Costalba
71e852ea81 Move game phase computation to MaterialInfo
Game phase is a strictly function of the material
combination so its natural place is MaterialInfo,
not position.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-14 17:57:49 +01:00
Marco Costalba
314faa905a Null move dynamic reduction based on value
After 994 games at 1+0

Mod vs Orig +244 =521 -229 50.75%  504.5/994 +5 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-14 17:57:34 +01:00
Marco Costalba
a530fc2b60 Use a more standard perft UCI interface
Call directly 'perft 6' to search up to depth 6*OnePly
instead of the old 'perft depth 6'.

It is more in line to what other engines do. Also a bit
of cleanup while there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-13 10:35:56 +01:00
Marco Costalba
7d0e0ff95e Better document king safety evaluation
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-13 10:10:22 +01:00
Marco Costalba
764229a2e2 Rearrange table layout in evaluate.cpp
A bit more cache friendly.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-13 10:10:21 +01:00
Marco Costalba
5e340346db Remove dcCandidates data member from SplitPoint
It is no more used now that we have CheckInfo.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-13 10:10:21 +01:00
Marco Costalba
bf395c6be1 Remove update_checkers()
Now that we have CheckInfo we don't need it anymore.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-11 22:26:29 +01:00
Marco Costalba
ad44ff2bca Retire evaluate_mobility()
Move the code to the caller and also move mob_area
computation out of evaluate_pieces(). It is more clear
the code flow and it is also faster.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-11 22:25:08 +01:00
Marco Costalba
8e96149c8c Small sort_moves() deobfuscation
Write the for loop in a more idiomatic way, no assembly
change and of course no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-11 22:25:07 +01:00
Marco Costalba
3c085775d7 Don't futility-prune ttMove
After 933 games
Mod vs Orig +219 =505 -208 +4 ELO

A small increase as expected.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-11 22:24:30 +01:00
Marco Costalba
dd5a3ae4a6 Propagate "move is check" info to do_move()
When false (common case) we avoid to update checkers
bitboard that although not so costly slows down a bit
this very hot and critical path.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-10 18:12:19 +01:00
Marco Costalba
f7f09b91ea Small update_checkers() cleanup
And is a bit faster too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-10 17:18:00 +01:00
Marco Costalba
8a116ce691 Small update to pop_1st_bit()
Avoid a 64 bit load using a pointer. It saves a couple of push/pop
instructions so advantage is only theorical, but anyway we use
pop_1st_bit() as a reference implementation for 32 bit systems so
we keep it more for documentation purposes then for other reasons.

Idea of pointer is of Eric Mullins.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-10 17:18:00 +01:00
Marco Costalba
16626dd655 Small CheckInfo fallout
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 21:48:02 +01:00
Marco Costalba
2f01d67a92 Fully convert move_is_check() internally
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 21:38:39 +01:00
Marco Costalba
975d5e9c64 Convert move_is_check() to take a CheckInfo reference
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 21:02:07 +01:00
Marco Costalba
30075e4abc Use CheckInfo to compute dcCandidates
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 20:54:45 +01:00
Marco Costalba
37398d9456 Introduce CheckInfo struct
Keeps info used to speed-up move_is_check()

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 20:50:02 +01:00
Marco Costalba
e05039156c Fix operator/(Score s, int i)
And remove some useless declarations

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 09:00:24 +01:00
Marco Costalba
f35ddb04af Don't copy the key in do_move
It will be overwritten anyway.

Also other little small touches that seem to increase
speed more then the whole enum Score patch series :-(

Optimization is really a black art.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 08:45:30 +01:00
Marco Costalba
ef6fca98a0 Define Score as an enum
Increases performance because now we use one integer
for both midgame and endgame scores.

Unfortunatly the latest patches seem to have reduced a bit
the speed so at the end we are more or less at the same
performance level of the beginning. But this patch series
introduced also some code cleanup so it is the main reason
we commit anyway.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-09 08:43:34 +01:00
Marco Costalba
fea46a8212 Change Score definition to avoid the union
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 21:10:49 +01:00
Marco Costalba
776c7df30c Revert "Do not extend at low depths if not in PV"
On Joona's QUAD:
Orig - Mod: 414 - 373

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 19:47:48 +01:00
Marco Costalba
5e112f16da Revert "IID in pv also when TT move depth is too small"
After almost 900 games we are at -2 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 19:46:36 +01:00
Marco Costalba
15ec3e911e Last conversions to Score in evaluate.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 17:08:28 +01:00
Marco Costalba
1ecd8e13ee Convert ThreatBonus to Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 15:45:49 +01:00
Marco Costalba
444c7c5183 Convert RookOn7thBonus and QueenOn7thBonus to be Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 15:35:11 +01:00
Marco Costalba
e9757f7610 Convert mobility bonus tables to Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 15:28:02 +01:00
Marco Costalba
1ab01f1c14 Convert apply_weight() to handle Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 14:33:40 +01:00
Marco Costalba
4626ec2890 Convert MaterialInfo and PawnInfo to use Score
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 14:17:10 +01:00
Marco Costalba
dda7e4639a Introduce PieceSquareTable[16][64]
Instead of MgPieceSquareTable[16][64] and EgPieceSquareTable[16][64]

This allows to fetch mg and eg values from adjacent words in memory.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 13:14:24 +01:00
Marco Costalba
1ae8c59c0b Convert Position to use Score struct
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 12:40:48 +01:00
Marco Costalba
06f06a9be8 Introduce Score struct
Save mid and end game scores in an union so to
operate on both values in one instruction.

This patch just introduces the infrastructure and changes
EvalInfo to use a single Score value instead of mgValue
and egValue.

Speed is more or less the same because we still don't use
unified midgame-endgame tables where the single assignment
optimization can prove effective.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 12:35:04 +01:00
Marco Costalba
2f5ee9e4e8 Fix correct name of int64_t type
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-07 10:08:28 +01:00
Marco Costalba
dd884b65b7 Do not extend at low depths if not in PV
Only check extensions are allowed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 17:52:12 +01:00
Marco Costalba
0fdc75c0bd IID in pv also when TT move depth is too small
Try an internal iterative deepening not only when we don't
have a TT move but also if search depth is more then 4*OnePly
higher then TT move depth.

On some tests it seems that in around 20% of cases ttMove changes !

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 17:52:04 +01:00
Marco Costalba
dae7cacd3b Better big-endian support wording in Makefile
Suggested by Joona.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 17:50:38 +01:00
Marco Costalba
7c0cb8e73d Enable POPCNT only through Makefile
Also remove some fallback templates that prevent a
compile error in case the user runs 'make icc-profile-popcnt'
from a non supported machine.

We want to loudly fail in that case instead of silently
fallback in a non-popcount compilation.

Updated documentation too.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 17:50:24 +01:00
Joona Kiiski
53ce6ce49c Add popcnt-support in Makefile
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 17:50:15 +01:00
Marco Costalba
7a68916ff9 Small code-style touches in movegen.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 14:42:48 +01:00
Marco Costalba
82a1e2d5fc Fix a small warning under icc
Variable 'f' in 'for' loop scope hides same named
one in outer scope.

Of curse no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 10:39:33 +01:00
Marco Costalba
dc286d2673 Big-endian compatible pop_1st_bit()
Thanks to Eric Mullins we have now endian friendly
pop_1st_bit() and also is removed the need to use
-fno-strict-aliasing compiler option with GCC.

Speed is almost as fast, very small difference if any in
perft test, so I assume almost no difference in real games.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 10:21:15 +01:00
Marco Costalba
a9e536a7eb Fix a compile error in debug mode
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-06 09:23:47 +01:00
Marco Costalba
e59d053984 Enable PH_TT_MOVES during evasion generation
This allow us to avoid the generation of the
evasion moves if we already have a TT move, and
in case we have a cut-off we skip evasion generation
altoghter.

Node count is changed because now we try TT move _before_
to generate evasions. The search on the TT move alters the
piece lists so that when we come back to generate evasions
we build the move list with a diferent order and this alters
the node count.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 19:14:17 +01:00
Marco Costalba
1c73c1c150 Extend move_is_legal() to work also when in check
This patch is a prerequisite to use TT phase
during evasions.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 14:58:57 +01:00
Marco Costalba
423b8b9ded Move locals definitions at the function start
It seems to me function are easier to read now.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 14:58:57 +01:00
Marco Costalba
94dcac1fee Retire MovePicker::discovered_check_candidates()
It is now no more needed to know dc candidates
inside MovePicker, so avoid calculating there.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 07:03:48 +01:00
Marco Costalba
0855d93de8 Rewrite generate_pawn_moves() and simplify evasions
Big cleanup and semplification of pawns evasions that
now are pseudo-legal as the remaining moves. This
allow us to remove a lot of tricky code.

Verified against perft: no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 07:03:47 +01:00
Marco Costalba
deecb3757c Generate pseudo-legal moves in generate_evasions()
This allow a big semplification in move generation
that will be committed with the next patch. And makes
handling of evasions similar to the other type of moves.

This patch plus the next seem to improve also on
the performance side because after 640 games to
verify there are no hidden regressions we are at +9 ELO

Verified with perft no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-05 07:03:36 +01:00
Marco Costalba
53c2bf0697 Optimize generate_evasions()
Generate captures of checking piece and blocking
evasions in one go.

Also reduce of one indentation level early returning
when we have a double check.

Verified with perft no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-04 11:18:43 +01:00
Marco Costalba
483a257618 Speed up perft
There is no need to do / undo the move at the last ply

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-04 11:18:05 +01:00
Marco Costalba
12461996a5 Remove SEE optimizations
Don't seem to help, perhaps because we
return an approximate SEE score instead of the
real negative score so that we have some bad capture
or evasion sub-optimal ordering that compensates
the speed up.

Anyhow after 999 games at 1+0
Mod vs Orig +240 =514 -245 -2 ELO

So almost no harm to remove and make the code simpler.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-04 11:17:42 +01:00
Marco Costalba
70b7404a63 Reorder evasions
Always try ttMove as first. Then try good captures ordered
by MVV/LVA, then non-captures if destination square is not
under attack, ordered by history value, and at the end
bad-captures and non-captures with a negative SEE. This
last group is ordered by the SEE score.

After 999 games at 1+0
Mod vs Orig +254 =546 -199 +19 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 21:08:30 +01:00
Marco Costalba
dddaeff7d8 Another see() shortcut
Because we only generate legal moves we can assume
a king cannot be recaptured, so we can safely return
immediately with the captured piece score. If the move
turns out to be illegal it will be pruned anyhow,
independently from SEE value. This gives a good speed up
especially now that we SEE-test all the evasions that
are always legal and very often are king moves.

Another optimization catches almost 15% of cases, unfortunatly
we have already calculated the very expensive attacks, so
benefits are not so big anyway.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 21:06:26 +01:00
Marco Costalba
941d923bf8 Shortcut see_sign() when SEE is known negative
This patch cuts 30% of SEE calculations, as a drawback
a returned negative value is no more always correct if
a shortcut is found.

This could impact move order when based on negative see
score as example bad captures and evasions.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 21:06:14 +01:00
Marco Costalba
23de3e16f1 Remove castling moves in check generation
Check generation is used only in qsearch and
only at Depth(0), castling moves that give check
are very rare overall and even almost not exsistent
at Depth(0).

So retire this almost never used code that adds
a small but consistent slow down in the normal path.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 17:19:04 +01:00
Marco Costalba
8ebe5075eb Optimize check generation
Because discovery checks are very rare it is better to handle
them all in one go and strip from usual check generation
function.

Also rewrite direct checks generation to use piece lists instead
of pop_1st_bit()

On perft test we have a +6% of speed up and is verified we
generate the same moves, although in a different order.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 17:13:01 +01:00
Joona Kiiski
fa49311b36 Implemented perft
Patch from Joona with extension to benchmark and inclusion
of Depth(0) moves generation by me.

Note that to test also qsearch and in particulary checks
generations a change in the end condition is needed.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-11-01 17:05:00 +01:00
Marco Costalba
d9b920acfb Evaluation threat values after 39089 games
Verified against tuning branch.

After 100 games at 1+0 on Joona QUAD

Mod - Orig: 527.5 - 471.5 (+20 elo)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-31 09:42:59 +01:00
Marco Costalba
12b0517d1b Fix build under gcc
Also some warnings squashed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-25 10:22:03 +01:00
Marco Costalba
b38685625a Add threat evaluation
Give a bonus for each kind of attacked piece. Bonus
value is based on the type of attacked piece and the
type of attacking one.

Penalize pieces attacked by enemy pawns, also in
this case penality value depends on the type of
attacked piece.

This patch oboletes as redundant the increased mobility
count of the attcked squares that is then removed.

After 956 games at 1+0
Mod vs Orig  +262 =462 -232 51.57%  493.0/956 +11 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-25 08:21:35 +01:00
Marco Costalba
73da3a431c Micro optimize mobility calculation
Take out of mobility loop a constant expression.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-23 11:24:53 +01:00
Marco Costalba
b50921fd5c Unify capture and promotion tests
Small code cleanup and a bit faster too.

The only functional change is that in extension
in pv node we extend promotions and not only captures
when condition met.

This is practically an undetectable change and has
no impact on strenght.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-22 07:22:54 +01:00
Marco Costalba
a72c55283d Don't prune TT move in qsearch even if SEE < 0
Even if SEE is negative there is always a good possibility
that TT move is a cut move anyway. For instance a lot of
BXN exchanges that have negative SEE can very easily be
good exchanges.

A nice side effect is a bit reduced frequency of
see_sign() calls.

After 643 games at 1+0
Mod vs Orig +174 =327 -142 52.49%  337.5/643 +17 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-22 07:22:44 +01:00
Marco Costalba
cf4df0327a Pick best moves one per cycle instead of sorting
When the move list is very small, like captures normally
are, it is faster to pick the best move with a linear
scan, one per cycle.

This has the added advantage that the picked capture move is
very possibly a cut-off move, so that other searches are
avoided. For non-captures it is still faster to sort in
advance.

Because scan-and-pick alghortim is not stable, node count
has changed.

After 885 games at 1+0
Mod vs Orig +196 =510 -179 50.96%  451.0/885

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-22 07:18:41 +01:00
Marco Costalba
51c3af9dd0 Avoid a needless locking in sp_search()
Only in less then 2% of cases we have a new sp->bestValue,
so check before to lock and save a costly locking
most of the times.

Patch suggested by Joona.

No functional search.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-19 07:47:18 +01:00
Joona Kiiski
f0b0a3b135 Similarize pruning code in search() and sp_search()
Use futility pruning also in split points.
Do not use history pruning in split points when
getting mated.

After 1000 games on Joona QUAD
Orig - Mod: 496 - 504

Added an optimization to avoid a costly lock in the
very common case that sp->futilityValue <= sp->bestValue.
A test on a dual CPU shows only 114 hits on 23196 events,
so avoid a lock in all the other cases.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-18 21:31:19 +01:00
Marco Costalba
4dd7fccfd1 Use an homegrown insertion sort instead of std::sort()
It is stable and it is also a bit faster then std::sort()
on the tipical small move lists that we need to handle.

Verified to have same functionality of std::stable_sort()

After 999 games at 1+0
Mod vs Orig +240 =534 -225 50.75%  507.0/999  +5 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-17 09:24:58 +01:00
Marco Costalba
c1ea5ed6f7 Do not prune the move if we are still under mate
If after the first tried 2 + int(depth) moves we still
have no any move that takes us out of a mate then do
not prune the following move, it is more important to
escape mate then speed up search.

This fixes an odd behaviour regarding mates, as example
the following diagram is a mate in 4, not in 3 as bogusly
reported before this patch.

1B2n3/8/2R5/5p2/3kp1n1/4p3/B3K3/8 w - - bm #4;

The performance impact should be minimal, the increment
in searched nodes is less then 0.1 %%

Idea and patch by Joona

After 999 games at 1+0
Mod vs Orig +193 =604 -202  -3 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-15 18:52:21 +01:00
Tord Romstad
53f882ff1a Minor improvement in eval of unstoppable pawns promoting one ply apart.
Marco's new code for evaluating two unstoppable passed pawns where
one pawn promotes a single ply before the other tried to detect
cases where the pawn that promotes first could immediately capture
the pawn that promotes a ply later, but didn't work in cases where
the two pawns are on the same file. An example of this is the
following position:

8/8/3K4/2P5/2p5/3k4/8/8 w - -

With the new code, such positions are handled correctly.
2009-10-15 12:39:55 +02:00
Marco Costalba
d8e7ce1863 Fix a crash when reaching PLY_MAX in a check position
In this case we call evaluate() being in check and this
is not allowed.

Bug found testing with reduced PLY_MAX value as suggested
by Miguel A. Ballicora on talkchess.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-12 15:32:50 +01:00
Marco Costalba
181d34e5a0 Add a new rule on promoting pawns in evaluate_passed_pawns()
Add a rule about the situation when one side queens exactly
one ply before the other. To avoid difficult (but luckly rare)
cases we only handle the case of free paths to queen for
both sides.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-12 10:09:06 +02:00
Marco Costalba
2655f93c32 Fix x-ray attack from behind in evaluate_passed_pawns()
Fix a condition for x-ray attack of a queen or
a rook behind a pawn of us. Previous condition does
not check if the enemy slider behind our pawn is
really attacking the pawn.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-12 09:36:35 +02:00
Marco Costalba
ab4d26f9bd Small cleanup and in evaluate_passed_pawns()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-12 09:36:30 +02:00
Marco Costalba
d6b04c2798 Revert "Use std::stable_sort() instead of std::sort()"
Unfortunatly std::stable_sort() implementation in gcc is
horrendously slow. We have a big performance regression on
Linux systems (-20% !)

So revert the commit and wait to fix the issue in a different
way, perhaps with an our home grown sorting, that should be
comparable in speed with std::sort()

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-12 09:36:23 +02:00
Marco Costalba
e2e249eabd Use std::stable_sort() instead of std::sort()
Standard does not mandate std::sort() to be stable, so we
can have, and actually do have different node count on
different platforms.

So use the platform independent std::stable_sort() and gain
same functionality on any platform (Windows, Unix, Mac OS)
and with any compiler (MSVC, gcc or Intel C++).

This sort is teoretically slower, but profiling shows only a very
minimal drop in performance, probably due to the fact that
the set to sort is very small, mainly only captures and with
less frequency non-captures, anyhow we are talking of 30-40 moves
in the worst average case. Sorting alghortims are build to work on
thousands or even milions of elements. With such small sets
performance difference seems not noticable.

After 999 games at 1+0

Mod vs Orig +234 =523 -242 -3 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-10 15:45:46 +01:00
Marco Costalba
ed19a9f909 Unroll color loops in evaluate_passed_pawns()
Speed increase is on 1.5% on Intel pgo build.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-10 14:56:56 +01:00
Marco Costalba
eddfd46a10 Use piece_list to scan the pawns in evaluate_pawns()
No functional change and small speed increase.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-10 14:56:10 +01:00
Marco Costalba
a806d7c3d6 Fix pieceList initialization in Position::clear()
We want piece list to be terminated with SQ_NONE.

This happens with all the pieces but the pawns that
being 8 make the inner loop exit just before writing
the SQ_NONE value at the tail of the list.

This bug was hidden because currently we don't use
piece list to scan pawns, but this will change in the
future and in any case an initialization should be done
correctly for the whole array to avoid subtle bugs in
the future.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-10 09:49:41 +01:00
Marco Costalba
ccdb634b77 Unroll color loops in get_pawn_info
This allow to resolve a lot of addresses at compile time
instead of an indirect access at runtime.

Speed up on pgo compile is of 1.3%

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 16:48:45 +01:00
Marco Costalba
564ed5b38c Small micro-optimization in get_pawn_info()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 11:29:11 +01:00
Marco Costalba
f05d059b17 Restore pliesFromNull counter
It is not equivalent because the check for the
50 moves rule get badly affected.

// Draw by the 50 moves rule?
if (st->rule50 > 100 || (st->rule50 == 100 && !is_check()))
    return true;

So we _really_ need two counters.

Thanks to Joona and Tord to be patience with a silly guy ;-)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 10:11:43 +01:00
Marco Costalba
06a5b602dc Fix an off-by-one bug in extract_pv()
In case we reach ply == PLY_MAX we exit the function
writing

pv[PLY_MAX] = MOVE_NONE;

And because SearchStack is defined as:

struct SearchStack {
  Move pv[PLY_MAX];
  Move currentMove;
  .....

We end up with the unwanted assignment

SearchStack.currentMove = MOVE_NONE;

Fortunatly this is harmless because currentMove is not used where
extarct_pv() is called. But neverthless this is a bug that
needs to be fixed.

Thanks to Uri Blass for spotting out this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 10:04:55 +01:00
Marco Costalba
d892063cd3 Rewrite previous patch using only one counter
Use only rule50 and retire pliesFromNull.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 04:35:11 +01:00
Marco Costalba
64d6ba2e98 Do not claim repetition after null move
Null moves can artificially create a repetition
draw where instead there is no one.

So use a second counter to reset history after
a null move.

Idea from Joona.

After 999 games at 1+0

Mod vs Orig +238 =553 -208 51.50%  514.5/999  +10 ELO

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-09 03:55:10 +01:00
Marco Costalba
5a5dc6fa10 Restore development version
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2009-10-08 15:16:47 +01:00
33 changed files with 2004 additions and 1770 deletions

View File

@@ -1,91 +1,90 @@
1. Introduction
---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI
(like XBoard with PolyGlot, eboard, Jos<6F>, Arena, Sigma Chess, Shredder,
Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
Stockfish with your GUI.
This version of Stockfish supports up to 8 CPUs, but has not been
tested thoroughly with more than 2. The program tries to detect the
number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the
"Threads" UCI parameter, and to make sure it equals the number of CPU
1. Introduction
---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program, but requires some UCI compatible GUI
(like XBoard with PolyGlot, eboard, Jos<6F>, Arena, Sigma Chess, Shredder,
Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
Stockfish with your GUI.
This version of Stockfish supports up to 8 CPUs, but has not been
tested thoroughly with more than 2. The program tries to detect the
number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the
"Threads" UCI parameter, and to make sure it equals the number of CPU
cores on your computer. If you are using more than four threads, it
is recommended to raise the value of "Minimum Split Depth" UCI parameter
to 6.
2. Files
--------
This distribution of Stockfish consists of the following files:
* Readme.txt, the file you are currently reading.
* Copying.txt, a text file containing the GNU General Public
License.
* src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like
systems. For further information about how to compile Stockfish
yourself, read section 4 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter.
3. Opening books
----------------
This version of Stockfish has experimental support for PolyGlot opening
books. For information about how to create such books, consult the
PolyGlot documentation. The book file can be selected by setting the
UCI parameter "Book File".
4. Compiling it yourself
------------------------
On Unix-like systems, it should usually be possible to compile
Stockfish directly from the source code with the included Makefile.
The exception is computer with big-endian CPUs, like PowerPC
Macintoshes. Some of the bitboard routines in the current version of
Stockfish are endianness-sensitive, and won't work on a big-endian CPU.
Stockfish has POPCNT instruction runtime detection and support. This can
give an extra speed on Core i7 or similar systems. To enable this feature
(disabled by default) simply uncomment #define USE_POPCNT in bitcount.h
before to compile.
On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
for bit counting. Detection is automatic at compile time, but in case you
experience compile problems you can comment out #define USE_BSFQ line in types.h
5. Terms of use
---------------
Stockfish is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or
using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Stockfish in
some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named
Copying.txt.
6. Feedback
-----------
The author's e-mail address is mcostalba@gmail.com
2. Files
--------
This distribution of Stockfish consists of the following files:
* Readme.txt, the file you are currently reading.
* Copying.txt, a text file containing the GNU General Public
License.
* src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like
systems. For further information about how to compile Stockfish
yourself, read section 4 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter.
3. Opening books
----------------
This version of Stockfish has experimental support for PolyGlot opening
books. For information about how to create such books, consult the
PolyGlot documentation. The book file can be selected by setting the
UCI parameter "Book File".
4. Compiling it yourself
------------------------
On Unix-like systems, it should usually be possible to compile
Stockfish directly from the source code with the included Makefile.
For big-endian machines like Power PC you need to enable the proper
flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
Stockfish has POPCNT instruction runtime detection and support. This can
give an extra speed on Core i7 or similar systems. To enable this feature
compile with 'make icc-profile-popcnt'
On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
for bit counting. Detection is automatic at compile time, but in case you
experience compile problems you can comment out #define USE_BSFQ line in types.h
5. Terms of use
---------------
Stockfish is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or
using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Stockfish in
some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named
Copying.txt.
6. Feedback
-----------
The author's e-mail address is mcostalba@gmail.com

View File

@@ -25,8 +25,11 @@ EXE = stockfish
### ==========================================================================
### Compiler speed switches for both GCC and ICC. These settings are generally
### fast on a broad range of systems, but may be changed experimentally
###
### NOTE: Some versions of gcc miscompile value.h with -O2 or -O3, this is the
### safe setup, try changing to -O3 or -O2 and verify it works for you.
### ==========================================================================
GCCFLAGS = -O3 -msse
GCCFLAGS = -O1 -msse
ICCFLAGS = -fast -msse
ICCFLAGS-OSX = -fast -mdynamic-no-pic
@@ -39,6 +42,14 @@ 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
@@ -47,9 +58,9 @@ PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### General compiler settings. Do not change
GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing
ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing -wd383,869,981,10187,10188,11505,11503
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing -wd383,869,981,10187,10188,11505,11503
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
@@ -74,6 +85,7 @@ help:
@echo "make > Default: Compiler = g++"
@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++"
@@ -133,6 +145,40 @@ icc-profile:
$(MAKE) icc-profile-use
@rm -rf profdir bench.txt
icc-profile-make-with-popcnt:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use-with-popcnt:
$(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) \
CXX='g++' \

View File

@@ -104,7 +104,7 @@ void benchmark(const string& commandLine) {
if (limitType == "time")
secsPerPos = val * 1000;
else if (limitType == "depth")
else if (limitType == "depth" || limitType == "perft")
maxDepth = val;
else
maxNodes = val;
@@ -153,7 +153,9 @@ void benchmark(const string& commandLine) {
int dummy[2] = {0, 0};
Position pos(*it);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (!think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
if (limitType == "perft")
totalNodes += perft(pos, maxDepth * OnePly);
else if (!think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
break;
totalNodes += nodes_searched();
}

View File

@@ -317,8 +317,8 @@ Square pop_1st_bit(Bitboard* b) {
#elif !defined(USE_BSFQ)
CACHE_LINE_ALIGNMENT
static const int BitTable[64] = {
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
@@ -336,30 +336,59 @@ union b_union {
Bitboard b;
struct {
#if defined (BIGENDIAN)
uint32_t h;
uint32_t l;
#else
uint32_t l;
uint32_t h;
#endif
} dw;
};
// WARNING: Needs -fno-strict-aliasing compiler option
Square pop_1st_bit(Bitboard* bb) {
b_union u;
b_union* u;
Square ret;
u.b = *bb;
u = (b_union*)bb;
if (u.dw.l)
{
*((uint32_t*)bb) = u.dw.l & (u.dw.l - 1);
return Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
}
*((uint32_t*)bb+1) = u.dw.h & (u.dw.h - 1); // Little endian only?
return Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
if (u->dw.l)
{
ret = Square(BitTable[((u->dw.l ^ (u->dw.l - 1)) * 0x783a9b23) >> 26]);
u->dw.l &= (u->dw.l - 1);
return ret;
}
ret = Square(BitTable[((~(u->dw.h ^ (u->dw.h - 1))) * 0x783a9b23) >> 26]);
u->dw.h &= (u->dw.h - 1);
return ret;
}
#endif
int bitScanReverse32(uint32_t b)
{
int result = 0;
if (b > 0xFFFF) {
b >>= 16;
result += 16;
}
if (b > 0xFF) {
b >>= 8;
result += 8;
}
if (b > 0xF) {
b >>= 4;
result += 4;
}
if (b > 0x3) {
b >>= 2;
result += 2;
}
return result + (b > 0) + (b > 1);
}
namespace {
// All functions below are used to precompute various bitboards during

View File

@@ -349,6 +349,7 @@ extern Square pop_1st_bit(Bitboard* b);
extern void print_bitboard(Bitboard b);
extern void init_bitboards();
extern int bitScanReverse32(uint32_t b);
#endif // !defined(BITBOARD_H_INCLUDED)

View File

@@ -22,19 +22,12 @@
#if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
// To enable POPCNT support uncomment USE_POPCNT define. For PGO compile on a Core i7
// you may want to collect profile data first with USE_POPCNT disabled and then, in a
// second profiling session, with USE_POPCNT enabled so to exercise both paths. Don't
// forget to leave USE_POPCNT enabled for the final optimized compile though ;-)
//#define USE_POPCNT
#include "types.h"
// Select type of intrinsic bit count instruction to use
// Select type of intrinsic bit count instruction to use, see
// README.txt on how to pgo compile with POPCNT support.
#if defined(__INTEL_COMPILER) && defined(IS_64BIT) && defined(USE_POPCNT) // Intel compiler
#if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
#include <nmmintrin.h>
@@ -45,17 +38,9 @@ inline bool cpu_has_popcnt() {
return (CPUInfo[2] >> 23) & 1;
}
// Define a dummy template to workaround a compile error if _mm_popcnt_u64() is not defined.
//
// If _mm_popcnt_u64() is defined in <nmmintrin.h> it will be choosen first due to
// C++ overload rules that always prefer a function to a template with the same name.
// If not, we avoid a compile error and because cpu_has_popcnt() should return false,
// our templetized _mm_popcnt_u64() is never called anyway.
template<typename T> inline unsigned _mm_popcnt_u64(T) { return 0; } // Is never called
#define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
#elif defined(_MSC_VER) && defined(IS_64BIT) && defined(USE_POPCNT) // Microsoft compiler
#elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
#include <intrin.h>
@@ -66,9 +51,6 @@ inline bool cpu_has_popcnt() {
return (CPUInfo[2] >> 23) & 1;
}
// See comment of _mm_popcnt_u64<>() few lines above for an explanation.
template<typename T> inline unsigned __popcnt64(T) { return 0; } // Is never called
#define POPCNT_INTRINSIC(x) __popcnt64(x)
#else // Safe fallback for unsupported compilers or when USE_POPCNT is disabled

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,8 @@
//// Includes
////
#include <iostream>
#include "material.h"
#include "pawns.h"
@@ -46,7 +48,7 @@ class Position;
struct EvalInfo {
// Middle game and endgame evaluations
Value mgValue, egValue;
Score value;
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
@@ -89,7 +91,7 @@ struct EvalInfo {
Move mateThreat[2];
// Middle game and endgame mobility scores.
Value mgMobility, egMobility;
Score mobility;
// Extra futility margin. This is added to the standard futility margin
// in the quiescence search.

View File

@@ -42,8 +42,6 @@ History::History() { clear(); }
void History::clear() {
memset(history, 0, 2 * 8 * 64 * sizeof(int));
memset(successCount, 0, 2 * 8 * 64 * sizeof(int));
memset(failureCount, 0, 2 * 8 * 64 * sizeof(int));
}
@@ -58,7 +56,6 @@ void History::success(Piece p, Square to, Depth d) {
assert(square_is_ok(to));
history[p][to] += int(d) * int(d);
successCount[p][to]++;
// Prevent history overflow
if (history[p][to] >= HistoryMax)
@@ -72,12 +69,14 @@ void History::success(Piece p, Square to, Depth d) {
/// called for each non-capturing move which failed to produce a beta cutoff
/// at a node where a beta cutoff was finally found.
void History::failure(Piece p, Square to) {
void History::failure(Piece p, Square to, Depth d) {
assert(piece_is_ok(p));
assert(square_is_ok(to));
failureCount[p][to]++;
history[p][to] -= int(d) * int(d);
if (history[p][to] < 0)
history[p][to] = 0;
}
@@ -91,15 +90,3 @@ int History::move_ordering_score(Piece p, Square to) const {
return history[p][to];
}
/// History::ok_to_prune() decides whether a move has been sufficiently
/// unsuccessful that it makes sense to prune it entirely.
bool History::ok_to_prune(Piece p, Square to, Depth d) const {
assert(piece_is_ok(p));
assert(square_is_ok(to));
return (int(d) * successCount[p][to] < failureCount[p][to]);
}

View File

@@ -47,14 +47,11 @@ public:
History();
void clear();
void success(Piece p, Square to, Depth d);
void failure(Piece p, Square to);
void failure(Piece p, Square to, Depth d);
int move_ordering_score(Piece p, Square to) const;
bool ok_to_prune(Piece p, Square to, Depth d) const;
private:
int history[16][64]; // [piece][square]
int successCount[16][64];
int failureCount[16][64];
};
@@ -64,7 +61,7 @@ private:
/// HistoryMax controls how often the history counters will be scaled down:
/// When the history score for a move gets bigger than HistoryMax, all
/// entries in the table are divided by 2. It is difficult to guess what
/// entries in the table are divided by 4. It is difficult to guess what
/// the ideal value of this constant is. Scaling down the scores often has
/// the effect that parts of the search tree which have been searched
/// recently have a bigger importance for move ordering than the moves which

View File

@@ -63,7 +63,7 @@ int main(int argc, char *argv[]) {
if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
cout << "Usage: stockfish bench <hash size> <threads> "
<< "[time = 60s] [fen positions file = default] "
<< "[time, depth or node limited = time] "
<< "[time, depth, perft or node limited = time] "
<< "[timing file name = none]" << endl;
else
{

View File

@@ -37,10 +37,15 @@ using namespace std;
namespace {
// Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998);
// Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][6] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
@@ -129,6 +134,22 @@ MaterialInfoTable::~MaterialInfoTable() {
}
/// MaterialInfoTable::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.
Phase MaterialInfoTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
if (npm >= MidgameLimit)
return PHASE_MIDGAME;
else if (npm <= EndgameLimit)
return PHASE_ENDGAME;
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
@@ -151,6 +172,9 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
mi->clear();
mi->key = key;
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
// Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed.
@@ -269,8 +293,8 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
Color c, them;
int sign;
int matValue = 0;
int sign, pt1, pt2, pc;
int v, vv, matValue = 0;
for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
{
@@ -304,25 +328,27 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
them = opposite_color(c);
v = 0;
// Second-degree polynomial material imbalance by Tord Romstad
//
// We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece",
// this allow us to be more flexible in defining bishop pair bonuses.
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
int c1 = sign * pieceCount[c][pt1];
if (!c1)
pc = pieceCount[c][pt1];
if (!pc)
continue;
matValue += c1 * LinearCoefficients[pt1];
vv = LinearCoefficients[pt1];
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
{
matValue += c1 * pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2];
matValue += c1 * pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
}
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
v += pc * vv;
}
matValue += sign * v;
}
mi->value = int16_t(matValue / 16);
return mi;

View File

@@ -51,9 +51,10 @@ class MaterialInfo {
public:
MaterialInfo() : key(0) { clear(); }
Value material_value() const;
Score material_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const;
Phase game_phase() const;
bool specialized_eval_exists() const;
Value evaluate(const Position& pos) const;
@@ -66,6 +67,7 @@ private:
EndgameEvaluationFunctionBase* evaluationFunction;
EndgameScalingFunctionBase* scalingFunction[2];
int spaceWeight;
Phase gamePhase;
};
/// The MaterialInfoTable class represents a pawn hash table. It is basically
@@ -81,6 +83,8 @@ public:
~MaterialInfoTable();
MaterialInfo* get_material_info(const Position& pos);
static Phase game_phase(const Position& pos);
private:
unsigned size;
MaterialInfo* entries;
@@ -92,12 +96,13 @@ private:
//// Inline functions
////
/// MaterialInfo::material_value simply returns the material balance
/// evaluation that is independent from game phase.
inline Value MaterialInfo::material_value() const {
inline Score MaterialInfo::material_value() const {
return Value(value);
return make_score(value, value);
}
@@ -141,6 +146,14 @@ inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
/// MaterialInfo::game_phase() returns the game phase according
/// to this material configuration.
inline Phase MaterialInfo::game_phase() const {
return gamePhase;
}
/// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration,

View File

@@ -50,7 +50,7 @@ using namespace std;
/// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
static const string EngineVersion = "1.5.1";
static const string EngineVersion = "1.6.1";
static const string AppName = "Stockfish";
static const string AppTag = "";

View File

@@ -62,9 +62,103 @@ struct MoveStack {
int score;
};
// Note that operator< is set up such that std::sort() will sort in descending order
// Note that operator< is set up such that sorting will be in descending order
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
// An helper insertion sort implementation
template<typename T>
inline void insertion_sort(T* firstMove, T* lastMove)
{
T value;
T *cur, *p, *d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (value < *p)
{
do *d = *p;
while (--d != firstMove && value < *--p);
*d = value;
}
}
}
// Our dedicated sort in range [firstMove, lastMove), it is well
// tuned for non-captures where we have a lot of zero scored moves.
template<typename T>
inline void sort_moves(T* firstMove, T* lastMove)
{
T tmp;
T *p, *d;
d = lastMove;
p = firstMove - 1;
d->score = -1; // right guard
// Split positives vs non-positives
do {
while ((++p)->score > 0);
if (p != d)
{
while (--d != p && d->score <= 0);
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort positives
insertion_sort<T>(firstMove, p);
d = lastMove;
p--;
// Split zero vs negatives
do {
while ((++p)->score == 0);
if (p != d)
{
while (--d != p && d->score < 0);
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort negatives
insertion_sort<T>(p, lastMove);
}
// Picks up the best move in range [curMove, lastMove), one per cycle.
// It is faster then sorting all the moves in advance when moves are few,
// as normally are the possible captures. Note that is not a stable alghoritm.
template<typename T>
inline T pick_best(T* curMove, T* lastMove)
{
T bestMove, tmp;
bestMove = *curMove;
while (++curMove != lastMove)
{
if (*curMove < bestMove)
{
tmp = *curMove;
*curMove = bestMove;
bestMove = tmp;
}
}
return bestMove;
}
////
//// Inline functions
@@ -82,6 +176,10 @@ inline PieceType move_promotion_piece(Move m) {
return PieceType((int(m) >> 12) & 7);
}
inline int move_is_special(Move m) {
return m & (0x1F << 12);
}
inline int move_is_promotion(Move m) {
return m & (7 << 12);
}

View File

@@ -52,16 +52,12 @@ namespace {
EVASION
};
// Functions
bool castling_is_check(const Position&, CastlingSide);
// Helper templates
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position&, MoveStack*);
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard = EmptyBoardBB,
Square = SQ_NONE, Bitboard = EmptyBoardBB);
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
// Template generate_piece_moves (captures and non-captures) with specializations and overloads
template<PieceType>
@@ -71,37 +67,29 @@ namespace {
MoveStack* generate_piece_moves<KING>(const Position&, MoveStack*, Color, Bitboard);
template<PieceType Piece, MoveType Type>
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us) {
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Piece == PAWN);
assert(Type == CAPTURE || Type == NON_CAPTURE);
assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m)
: generate_pawn_moves<BLACK, Type>(p, m));
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
}
// Template generate_piece_checks with specializations
// Templates for non-capture checks generation
template<PieceType Piece>
MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square);
template<PieceType>
MoveStack* generate_piece_checks(const Position&, MoveStack*, Color, Bitboard, Square);
MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square);
template<>
inline MoveStack* generate_piece_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
inline MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
return (us == WHITE ? generate_pawn_moves<WHITE, CHECK>(p, m, dc, ksq)
: generate_pawn_moves<BLACK, CHECK>(p, m, dc, ksq));
}
// Template generate_piece_evasions with specializations
template<PieceType>
MoveStack* generate_piece_evasions(const Position&, MoveStack*, Color, Bitboard, Bitboard);
template<>
inline MoveStack* generate_piece_evasions<PAWN>(const Position& p, MoveStack* m,
Color us, Bitboard t, Bitboard pnd) {
return (us == WHITE ? generate_pawn_moves<WHITE, EVASION>(p, m, pnd, SQ_NONE, t)
: generate_pawn_moves<BLACK, EVASION>(p, m, pnd, SQ_NONE, t));
}
}
@@ -125,7 +113,7 @@ MoveStack* generate_captures(const Position& pos, MoveStack* mlist) {
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us);
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
return generate_piece_moves<KING>(pos, mlist, us, target);
}
@@ -141,7 +129,7 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
Color us = pos.side_to_move();
Bitboard target = pos.empty_squares();
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us);
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
@@ -152,175 +140,111 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
}
/// generate_non_capture_checks() generates all pseudo-legal non-captures and
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) {
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us));
assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING));
// Pieces moves
mlist = generate_piece_checks<PAWN>(pos, mlist, us, dc, ksq);
mlist = generate_piece_checks<KNIGHT>(pos, mlist, us, dc, ksq);
mlist = generate_piece_checks<BISHOP>(pos, mlist, us, dc, ksq);
mlist = generate_piece_checks<ROOK>(pos, mlist, us, dc, ksq);
mlist = generate_piece_checks<QUEEN>(pos, mlist, us, dc, ksq);
mlist = generate_piece_checks<KING>(pos, mlist, us, dc, ksq);
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us);
// Castling moves that give check. Very rare but nice to have!
if ( pos.can_castle_queenside(us)
&& (square_rank(ksq) == square_rank(pos.king_square(us)) || square_file(ksq) == FILE_D)
&& castling_is_check(pos, QUEEN_SIDE))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist);
while (b)
{
from = pop_1st_bit(&b);
switch (pos.type_of_piece_on(from))
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break;
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
default: assert(false); break;
}
}
if ( pos.can_castle_kingside(us)
&& (square_rank(ksq) == square_rank(pos.king_square(us)) || square_file(ksq) == FILE_F)
&& castling_is_check(pos, KING_SIDE))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
return mlist;
// Direct non-capture checks
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
}
/// generate_evasions() generates all check evasions when the side to move is
/// in check. Unlike the other move generation functions, this one generates
/// only legal moves. Returns a pointer to the end of the move list.
/// generate_evasions() generates all pseudo-legal check evasions when
/// the side to move is in check. Returns a pointer to the end of the move list.
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) {
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.is_check());
Square from, to;
Bitboard b, target;
Square from, checksq;
int checkersCnt = 0;
Color us = pos.side_to_move();
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
Bitboard sliderAttacks = EmptyBoardBB;
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB;
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
// The bitboard of occupied pieces without our king
Bitboard b_noKing = pos.occupied_squares();
clear_bit(&b_noKing, ksq);
assert(checkers);
// Find squares attacked by slider checkers, we will remove
// them from the king evasions set so to avoid a couple
// of cycles in the slow king evasions legality check loop
// and to be able to use attackers_to().
Bitboard b = checkers & pos.pieces(BISHOP, QUEEN);
while (b)
// them from the king evasions set so to early skip known
// illegal moves and avoid an useless legality check later.
b = checkers;
do
{
from = pop_1st_bit(&b);
sliderAttacks |= bishop_attacks_bb(from, b_noKing);
}
checkersCnt++;
checksq = pop_1st_bit(&b);
b = checkers & pos.pieces(ROOK, QUEEN);
while (b)
{
from = pop_1st_bit(&b);
sliderAttacks |= rook_attacks_bb(from, b_noKing);
}
assert(pos.color_of_piece_on(checksq) == opposite_color(us));
switch (pos.type_of_piece_on(checksq))
{
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN:
// In case of a queen remove also squares attacked in the other direction to
// avoid possible illegal moves when queen and king are on adjacent squares.
if (direction_is_straight(checksq, ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
}
} while (b);
// Generate evasions for king, capture and non capture moves
Bitboard enemy = pos.pieces_of_color(them);
Bitboard b1 = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
while (b1)
{
// Note that we can use attackers_to() only because we have already
// removed from b1 the squares attacked by slider checkers.
to = pop_1st_bit(&b1);
if (!(pos.attackers_to(to) & enemy))
(*mlist++).move = make_move(ksq, to);
}
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
from = ksq;
SERIALIZE_MOVES(b);
// Generate evasions for other pieces only if not double check. We use a
// simple bit twiddling hack here rather than calling count_1s in order to
// save some time (we know that pos.checkers() has at most two nonzero bits).
if (!(checkers & (checkers - 1))) // Only one bit set?
{
Square checksq = first_1(checkers);
// Generate evasions for other pieces only if not double check
if (checkersCnt > 1)
return mlist;
assert(pos.color_of_piece_on(checksq) == them);
// Find squares where a blocking evasion or a capture of the
// checker piece is possible.
target = squares_between(checksq, ksq) | checkers;
// Generate captures of the checking piece
// Pawn captures
b1 = pos.attacks_from<PAWN>(checksq, them) & pos.pieces(PAWN, us) & ~pinned;
while (b1)
{
from = pop_1st_bit(&b1);
if (relative_rank(us, checksq) == RANK_8)
{
(*mlist++).move = make_promotion_move(from, checksq, QUEEN);
(*mlist++).move = make_promotion_move(from, checksq, ROOK);
(*mlist++).move = make_promotion_move(from, checksq, BISHOP);
(*mlist++).move = make_promotion_move(from, checksq, KNIGHT);
} else
(*mlist++).move = make_move(from, checksq);
}
// Pieces captures
b1 = ( (pos.attacks_from<KNIGHT>(checksq) & pos.pieces(KNIGHT, us))
| (pos.attacks_from<BISHOP>(checksq) & pos.pieces(BISHOP, QUEEN, us))
| (pos.attacks_from<ROOK>(checksq) & pos.pieces(ROOK, QUEEN, us)) ) & ~pinned;
while (b1)
{
from = pop_1st_bit(&b1);
(*mlist++).move = make_move(from, checksq);
}
// Blocking check evasions are possible only if the checking piece is a slider
if (sliderAttacks)
{
Bitboard blockSquares = squares_between(checksq, ksq);
assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB);
if (blockSquares)
{
mlist = generate_piece_evasions<PAWN>(pos, mlist, us, blockSquares, pinned);
mlist = generate_piece_evasions<KNIGHT>(pos, mlist, us, blockSquares, pinned);
mlist = generate_piece_evasions<BISHOP>(pos, mlist, us, blockSquares, pinned);
mlist = generate_piece_evasions<ROOK>(pos, mlist, us, blockSquares, pinned);
mlist = generate_piece_evasions<QUEEN>(pos, mlist, us, blockSquares, pinned);
}
}
// Finally, the special case of en passant captures. An en passant
// capture can only be a check evasion if the check is not a discovered
// check. If pos.ep_square() is set, the last move made must have been
// a double pawn push. If, furthermore, the checking piece is a pawn,
// an en passant check evasion may be possible.
if (pos.ep_square() != SQ_NONE && (checkers & pos.pieces(PAWN, them)))
{
to = pos.ep_square();
b1 = pos.attacks_from<PAWN>(to, them) & pos.pieces(PAWN, us);
// The checking pawn cannot be a discovered (bishop) check candidate
// otherwise we were in check also before last double push move.
assert(!bit_is_set(pos.discovered_check_candidates(them), checksq));
assert(count_1s(b1) == 1 || count_1s(b1) == 2);
b1 &= ~pinned;
while (b1)
{
from = pop_1st_bit(&b1);
// Move is always legal because checking pawn is not a discovered
// check candidate and our capturing pawn has been already tested
// against pinned pieces.
(*mlist++).move = make_ep_move(from, to);
}
}
}
return mlist;
mlist = generate_piece_moves<PAWN, EVASION>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
return generate_piece_moves<QUEEN>(pos, mlist, us, target);
}
@@ -332,24 +256,25 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
assert(pos.is_ok());
MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
if (pos.is_check())
return generate_evasions(pos, mlist, pinned);
// Generate pseudo-legal moves
MoveStack* last = generate_captures(pos, mlist);
last = generate_noncaptures(pos, last);
if (pos.is_check())
last = generate_evasions(pos, mlist);
else
last = generate_noncaptures(pos, generate_captures(pos, mlist));
if (pseudoLegal)
return last;
// Remove illegal moves from the list
for (MoveStack* cur = mlist; cur != last; cur++)
if (!pos.pl_move_is_legal(cur->move, pinned))
{
while (cur != last)
if (pos.pl_move_is_legal(cur->move, pinned))
cur++;
else
cur->move = (--last)->move;
cur--;
}
return last;
}
@@ -361,10 +286,11 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
bool move_is_legal(const Position& pos, const Move m) {
MoveStack mlist[256];
MoveStack* last = generate_moves(pos, mlist, true);
for (MoveStack* cur = mlist; cur != last; cur++)
MoveStack *cur, *last = generate_moves(pos, mlist, true);
for (cur = mlist; cur != last; cur++)
if (cur->move == m)
return pos.pl_move_is_legal(m);
return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));
return false;
}
@@ -372,25 +298,23 @@ bool move_is_legal(const Position& pos, const Move m) {
/// Fast version of move_is_legal() that takes a position a move and a
/// bitboard of pinned pieces as input, and tests whether the move is legal.
/// This version must only be used when the side to move is not in check.
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
assert(pos.is_ok());
assert(!pos.is_check());
assert(move_is_ok(m));
assert(pinned == pos.pinned_pieces(pos.side_to_move()));
// Use a slower but simpler function for uncommon cases
if (move_is_ep(m) || move_is_castle(m))
return move_is_legal(pos, m);
Color us = pos.side_to_move();
Color them = opposite_color(us);
Square from = move_from(m);
Square to = move_to(m);
Piece pc = pos.piece_on(from);
// Use a slower but simpler function for uncommon cases
if (move_is_ep(m) || move_is_castle(m))
return move_is_legal(pos, m);
// If the from square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
if (color_of_piece(pc) != us)
@@ -459,13 +383,13 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
return false;
}
// The move is pseudo-legal, check if it is also legal
return pos.pl_move_is_legal(m, pinned);
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
}
// Luckly we can handle all the other pieces in one go
return ( bit_is_set(pos.attacks_from(pc, from), to)
&& pos.pl_move_is_legal(m, pinned)
&& !move_is_promotion(m));
return bit_is_set(pos.attacks_from(pc, from), to)
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
&& !move_is_promotion(m);
}
@@ -474,8 +398,8 @@ namespace {
template<PieceType Piece>
MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Square from;
Bitboard b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
while ((from = *ptr++) != SQ_NONE)
@@ -497,24 +421,6 @@ namespace {
return mlist;
}
template<PieceType Piece>
MoveStack* generate_piece_evasions(const Position& pos, MoveStack* mlist,
Color us, Bitboard target, Bitboard pinned) {
Square from;
Bitboard b;
const Square* ptr = pos.piece_list_begin(us, Piece);
while ((from = *ptr++) != SQ_NONE)
{
if (pinned && bit_is_set(pinned, from))
continue;
b = pos.attacks_from<Piece>(from) & target;
SERIALIZE_MOVES(b);
}
return mlist;
}
template<Color Us, SquareDelta Direction>
inline Bitboard move_pawns(Bitboard p) {
@@ -528,8 +434,8 @@ namespace {
return p;
}
template<Color Us, SquareDelta Diagonal>
MoveStack* generate_pawn_diagonal_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool promotion) {
template<Color Us, MoveType Type, SquareDelta Diagonal>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
// Calculate our parametrized parameters at compile time
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
@@ -538,136 +444,152 @@ namespace {
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
Bitboard b1, b2;
Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
Bitboard b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
// Capturing promotions
if (promotion)
// Capturing promotions and under-promotions
if (b1 & TRank8BB)
{
Bitboard b2 = b1 & TRank8BB;
b2 = b1 & TRank8BB;
b1 &= ~TRank8BB;
while (b2)
{
to = pop_1st_bit(&b2);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion. It is not sure that
// the promoted knight will give check, but it doesn't worth to verify.
if (Type == CHECK)
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
}
}
// Capturing non-promotions
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
// Serialize standard captures
if (Type == CAPTURE || Type == EVASION)
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard dcp,
Square ksq, Bitboard blockSquares) {
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
Bitboard b1, b2, dcPawns1, dcPawns2;
Square to;
Bitboard pawns = (Type == EVASION ? pos.pieces(PAWN, Us) & ~dcp : pos.pieces(PAWN, Us));
bool possiblePromotion = pawns & TRank7BB;
Bitboard b1, b2, enemyPieces, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
if (Type == CAPTURE)
// Standard captures and capturing promotions and underpromotions
if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
{
// Standard captures and capturing promotions in both directions
Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us));
mlist = generate_pawn_diagonal_captures<Us, DELTA_NE>(mlist, pawns, enemyPieces, possiblePromotion);
mlist = generate_pawn_diagonal_captures<Us, DELTA_NW>(mlist, pawns, enemyPieces, possiblePromotion);
enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
if (Type == EVASION)
enemyPieces &= target; // Capture only the checker piece
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
}
if (possiblePromotion)
// Non-capturing promotions and underpromotions
if (pawns & TRank7BB)
{
// When generating checks consider under-promotion moves (both captures
// and non captures) only if can give a discovery check. Note that dcp
// is dc bitboard or pinned bitboard when Type == EVASION.
Bitboard pp = (Type == CHECK ? pawns & dcp : pawns);
b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
if (Type != EVASION && Type != CAPTURE)
{
Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us));
// Underpromotion captures in the a1-h8 (a8-h1 for black) direction
b1 = move_pawns<Us, DELTA_NE>(pp) & ~FileABB & enemyPieces & TRank8BB;
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, ROOK);
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, KNIGHT);
}
// Underpromotion captures in the h1-a8 (h8-a1 for black) direction
b1 = move_pawns<Us, DELTA_NW>(pp) & ~FileHBB & enemyPieces & TRank8BB;
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, ROOK);
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, KNIGHT);
}
}
// Underpromotion pawn pushes. Also queen promotions for evasions and captures.
b1 = move_pawns<Us, DELTA_N>(pp) & TRank8BB;
b1 &= (Type == EVASION ? blockSquares : pos.empty_squares());
if (Type == EVASION)
b1 &= target; // Only blocking promotion pushes
while (b1)
{
to = pop_1st_bit(&b1);
if (Type == EVASION || Type == CAPTURE)
if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
if (Type != CAPTURE)
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
}
}
// Standard pawn pushes and double pushes
if (Type != CAPTURE)
{
Bitboard emptySquares = pos.empty_squares();
dcPawns1 = dcPawns2 = EmptyBoardBB;
if (Type == CHECK && (pawns & dcp))
{
// Pawn moves which gives discovered check. This is possible only if the
// pawn is not on the same file as the enemy king, because we don't
// generate captures.
dcPawns1 = move_pawns<Us, DELTA_N>(pawns & dcp & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
dcPawns2 = move_pawns<Us, DELTA_N>(dcPawns1 & TRank3BB) & emptySquares;
}
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
// Single pawn pushes
// Single and double pawn pushes
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
b2 = (Type == CHECK ? (b1 & pos.attacks_from<PAWN>(ksq, Them)) | dcPawns1 :
(Type == EVASION ? b1 & blockSquares : b1));
SERIALIZE_MOVES_D(b2, -TDELTA_N);
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
// Double pawn pushes
b1 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
b2 = (Type == CHECK ? (b1 & pos.attacks_from<PAWN>(ksq, Them)) | dcPawns2 :
(Type == EVASION ? b1 & blockSquares : b1));
// Filter out unwanted pushes according to the move type
if (Type == EVASION)
{
b1 &= target;
b2 &= target;
}
else if (Type == CHECK)
{
// Pawn moves which give direct cheks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Pawn moves which gives discovered check. This is possible only if
// the pawn is not on the same file as the enemy king, because we
// don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard
{
Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
}
else if (pos.ep_square() != SQ_NONE) // En passant captures
// En passant captures
if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB);
while (b1)
@@ -680,43 +602,49 @@ namespace {
}
template<PieceType Piece>
MoveStack* generate_piece_checks(const Position& pos, MoveStack* mlist, Color us,
MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Piece != QUEEN);
Bitboard b = pos.attacks_from<Piece>(from) & pos.empty_squares();
if (Piece == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Piece>
MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Piece != KING);
Bitboard target = pos.pieces(Piece, us);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
// Discovered non-capture checks
Bitboard b = target & dc;
if ((from = *ptr++) == SQ_NONE)
return mlist;
assert(Piece != QUEEN || !b);
checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
while (b)
do
{
Square from = pop_1st_bit(&b);
Bitboard bb = pos.attacks_from<Piece>(from) & pos.empty_squares();
if (Piece == KING)
bb &= ~QueenPseudoAttacks[ksq];
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
SERIALIZE_MOVES(bb);
}
if (dc && bit_is_set(dc, from))
continue;
// Direct non-capture checks
b = target & ~dc;
Bitboard checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
if (Piece != KING && checkSqs)
{
while (b)
{
Square from = pop_1st_bit(&b);
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
b = pos.attacks_from<Piece>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
Bitboard bb = pos.attacks_from<Piece>(from) & checkSqs;
SERIALIZE_MOVES(bb);
}
}
return mlist;
}
@@ -762,17 +690,4 @@ namespace {
}
return mlist;
}
bool castling_is_check(const Position& pos, CastlingSide side) {
// After castling opponent king is attacked by the castled rook?
File rookFile = (side == QUEEN_SIDE ? FILE_D : FILE_F);
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, ksq); // Remove our king from the board
Square rsq = make_square(rookFile, square_rank(ksq));
return bit_is_set(rook_attacks_bb(rsq, occ), pos.king_square(opposite_color(us)));
}
}

View File

@@ -34,8 +34,8 @@
extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc);
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned);
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
extern bool move_is_legal(const Position& pos, const Move m);

View File

@@ -23,7 +23,6 @@
//// Includes
////
#include <algorithm>
#include <cassert>
#include "history.h"
@@ -53,7 +52,7 @@ namespace {
CACHE_LINE_ALIGNMENT
const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const uint8_t EvasionsPhaseTable[] = { PH_EVASIONS, PH_STOP};
const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
}
@@ -78,7 +77,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
finished = false;
lastBadCapture = badCaptures;
if (ss)
pinned = p.pinned_pieces(pos.side_to_move());
if (ss && !p.is_check())
{
ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
@@ -87,21 +88,16 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
} else
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
Color us = pos.side_to_move();
dc = p.discovered_check_candidates(us);
pinned = p.pinned_pieces(us);
if (p.is_check())
phasePtr = EvasionsPhaseTable;
else if (d > Depth(0))
phasePtr = MainSearchPhaseTable + !searchTT;
phasePtr = MainSearchPhaseTable;
else if (d == Depth(0))
phasePtr = QsearchWithChecksPhaseTable + !searchTT;
phasePtr = QsearchWithChecksPhaseTable;
else
phasePtr = QsearchWithoutChecksPhaseTable + !searchTT;
phasePtr = QsearchWithoutChecksPhaseTable;
phasePtr--;
phasePtr += !searchTT - 1;
go_next_phase();
}
@@ -123,7 +119,6 @@ void MovePicker::go_next_phase() {
case PH_GOOD_CAPTURES:
lastMove = generate_captures(pos, moves);
score_captures();
std::sort(moves, lastMove);
return;
case PH_KILLERS:
@@ -134,7 +129,7 @@ void MovePicker::go_next_phase() {
case PH_NONCAPTURES:
lastMove = generate_noncaptures(pos, moves);
score_noncaptures();
std::sort(moves, lastMove);
sort_moves(moves, lastMove);
return;
case PH_BAD_CAPTURES:
@@ -142,25 +137,22 @@ void MovePicker::go_next_phase() {
// to get SEE move ordering.
curMove = badCaptures;
lastMove = lastBadCapture;
std::sort(badCaptures, lastMove);
return;
case PH_EVASIONS:
assert(pos.is_check());
lastMove = generate_evasions(pos, moves, pinned);
lastMove = generate_evasions(pos, moves);
score_evasions();
std::sort(moves, lastMove);
return;
case PH_QCAPTURES:
lastMove = generate_captures(pos, moves);
score_captures();
std::sort(moves, lastMove);
return;
case PH_QCHECKS:
// Perhaps we should order moves move here? FIXME
lastMove = generate_non_capture_checks(pos, moves, dc);
lastMove = generate_non_capture_checks(pos, moves);
return;
case PH_STOP:
@@ -202,8 +194,8 @@ void MovePicker::score_captures() {
if (move_is_promotion(m))
cur->score = QueenValueMidgame;
else
cur->score = int(pos.midgame_value_of_piece_on(move_to(m)))
-int(pos.type_of_piece_on(move_from(m)));
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m));
}
}
@@ -229,24 +221,27 @@ void MovePicker::score_noncaptures() {
hs += 1000;
// pst based scoring
cur->score = hs + pos.pst_delta<Position::MidGame>(piece, from, to);
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
}
}
void MovePicker::score_evasions() {
// Try good captures ordered by MVV/LVA, then non-captures if
// destination square is not under attack, ordered by history
// value, and at the end bad-captures and non-captures with a
// negative SEE. This last group is ordered by the SEE score.
Move m;
int seeScore;
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
if (m == ttMoves[0].move)
cur->score = 2 * HistoryMax;
else if (!pos.square_is_empty(move_to(m)))
{
int seeScore = pos.see(m);
cur->score = seeScore + (seeScore >= 0 ? HistoryMax : 0);
} else
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore;
else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + HistoryMax;
else
cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
}
}
@@ -259,26 +254,23 @@ void MovePicker::score_evasions() {
Move MovePicker::get_next_move() {
assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP);
assert( pos.is_check() || *phasePtr != PH_EVASIONS);
Move move;
while (true)
{
while (curMove != lastMove)
{
move = (curMove++)->move;
switch (phase) {
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned))
return move;
break;
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
@@ -298,6 +290,7 @@ Move MovePicker::get_next_move() {
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& move != ttMoves[0].move
&& move != ttMoves[1].move
@@ -307,6 +300,7 @@ Move MovePicker::get_next_move() {
break;
case PH_NONCAPTURES:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move
@@ -315,13 +309,20 @@ Move MovePicker::get_next_move() {
return move;
break;
case PH_EVASIONS:
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_QCHECKS:
// Maybe postpone the legality check until after futility pruning?
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;

View File

@@ -54,7 +54,6 @@ public:
Move get_next_move();
Move get_next_move(Lock& lock);
int number_of_evasions() const;
Bitboard discovered_check_candidates() const;
private:
void score_captures();
@@ -69,7 +68,7 @@ private:
int phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastBadCapture;
Bitboard dc, pinned;
Bitboard pinned;
MoveStack moves[256], badCaptures[64];
};
@@ -88,12 +87,4 @@ inline int MovePicker::number_of_evasions() const {
return int(lastMove - moves);
}
/// MovePicker::discovered_check_candidates() returns a bitboard containing
/// all pieces which can possibly give discovered check. This bitboard is
/// computed by the constructor function.
inline Bitboard MovePicker::discovered_check_candidates() const {
return dc;
}
#endif // !defined(MOVEPICK_H_INCLUDED)

View File

@@ -38,64 +38,36 @@ namespace {
/// Constants and variables
// Doubled pawn penalty by file, middle game
const Value DoubledPawnMidgamePenalty[8] = {
Value(13), Value(20), Value(23), Value(23),
Value(23), Value(23), Value(20), Value(13)
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by file
const Score DoubledPawnPenalty[8] = {
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43)
};
// Doubled pawn penalty by file, endgame
const Value DoubledPawnEndgamePenalty[8] = {
Value(43), Value(48), Value(48), Value(48),
Value(48), Value(48), Value(48), Value(43)
// Isolated pawn penalty by file
const Score IsolatedPawnPenalty[8] = {
S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30)
};
// Isolated pawn penalty by file, middle game
const Value IsolatedPawnMidgamePenalty[8] = {
Value(25), Value(36), Value(40), Value(40),
Value(40), Value(40), Value(36), Value(25)
// Backward pawn penalty by file
const Score BackwardPawnPenalty[8] = {
S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28)
};
// Isolated pawn penalty by file, endgame
const Value IsolatedPawnEndgamePenalty[8] = {
Value(30), Value(35), Value(35), Value(35),
Value(35), Value(35), Value(35), Value(30)
// Pawn chain membership bonus by file
const Score ChainBonus[8] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
};
// Backward pawn penalty by file, middle game
const Value BackwardPawnMidgamePenalty[8] = {
Value(20), Value(29), Value(33), Value(33),
Value(33), Value(33), Value(29), Value(20)
};
// Backward pawn penalty by file, endgame
const Value BackwardPawnEndgamePenalty[8] = {
Value(28), Value(31), Value(31), Value(31),
Value(31), Value(31), Value(31), Value(28)
};
// Pawn chain membership bonus by file, middle game
const Value ChainMidgameBonus[8] = {
Value(11), Value(13), Value(13), Value(14),
Value(14), Value(13), Value(13), Value(11)
};
// Pawn chain membership bonus by file, endgame
const Value ChainEndgameBonus[8] = {
Value(-1), Value(-1), Value(-1), Value(-1),
Value(-1), Value(-1), Value(-1), Value(-1)
};
// Candidate passed pawn bonus by rank, middle game
const Value CandidateMidgameBonus[8] = {
Value( 0), Value( 6), Value(6), Value(14),
Value(34), Value(83), Value(0), Value( 0)
};
// Candidate passed pawn bonus by rank, endgame
const Value CandidateEndgameBonus[8] = {
Value( 0), Value( 13), Value(13), Value(29),
Value(68), Value(166), Value( 0), Value( 0)
// Candidate passed pawn bonus by rank
const Score CandidateBonus[8] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0)
};
// Pawn storm tables for positions with opposite castling
@@ -190,207 +162,194 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
pi->clear();
pi->key = key;
Value mgValue[2] = {Value(0), Value(0)};
Value egValue[2] = {Value(0), Value(0)};
// Calculate pawn attacks
pi->pawnAttacks[WHITE] = ((pos.pieces(PAWN, WHITE) << 9) & ~FileABB) | ((pos.pieces(PAWN, WHITE) << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((pos.pieces(PAWN, BLACK) >> 7) & ~FileABB) | ((pos.pieces(PAWN, BLACK) >> 9) & ~FileHBB);
Bitboard whitePawns = pos.pieces(PAWN, WHITE);
Bitboard blackPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
// Loop through the pawns for both colors
for (Color us = WHITE; us <= BLACK; us++)
{
Color them = opposite_color(us);
Bitboard ourPawns = pos.pieces(PAWN, us);
Bitboard theirPawns = pos.pieces(PAWN, them);
Bitboard pawns = ourPawns;
// Initialize pawn storm scores by giving bonuses for open files
for (File f = FILE_A; f <= FILE_H; f++)
if (!(pawns & file_bb(f)))
{
pi->ksStormValue[us] += KStormOpenFileBonus[f];
pi->qsStormValue[us] += QStormOpenFileBonus[f];
pi->halfOpenFiles[us] |= (1 << f);
}
// Loop through all pawns of the current color and score each pawn
while (pawns)
{
Square s = pop_1st_bit(&pawns);
File f = square_file(s);
Rank r = square_rank(s);
assert(pos.piece_on(s) == piece_of_color_and_type(us, PAWN));
// Passed, isolated or doubled pawn?
bool passed = Position::pawn_is_passed(theirPawns, us, s);
bool isolated = Position::pawn_is_isolated(ourPawns, s);
bool doubled = Position::pawn_is_doubled(ourPawns, us, s);
// We calculate kingside and queenside pawn storm
// scores for both colors. These are used when evaluating
// 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
int bonus = KStormTable[relative_square(us, s)];
if (f >= FILE_F)
{
Bitboard b = outpost_mask(us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
while (b)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_H)
bonus += 4*StormLeverBonus[f] - 8*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 4*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 2*square_distance(s, s2);
}
}
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)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_A)
bonus += 4*StormLeverBonus[f] - 16*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 8*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 4*square_distance(s, s2);
}
}
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.
bool chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (us == WHITE ? 1 : -1)));
// Test for backward pawn
//
// If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot
// be backward either.
bool backward;
if ( passed
|| isolated
|| chain
|| (pos.attacks_from<PAWN>(s, us) & theirPawns)
|| (ourPawns & behind_bb(us, r) & neighboring_files_bb(f)))
backward = false;
else
{
// We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b = pos.attacks_from<PAWN>(s, us);
if (us == WHITE)
{
for ( ; !(b & (ourPawns | theirPawns)); b <<= 8);
backward = (b | (b << 8)) & theirPawns;
}
else
{
for ( ; !(b & (ourPawns | theirPawns)); b >>= 8);
backward = (b | (b >> 8)) & theirPawns;
}
}
// Test for candidate passed pawn
bool candidate;
candidate = !passed
&& !(theirPawns & file_bb(f))
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(us, r) | rank_bb(r)) & ourPawns)
- 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
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if (passed && (ourPawns & squares_in_front_of(us, s)))
passed = false;
// Score this pawn
if (passed)
set_bit(&(pi->passedPawns), s);
if (isolated)
{
mgValue[us] -= IsolatedPawnMidgamePenalty[f];
egValue[us] -= IsolatedPawnEndgamePenalty[f];
if (!(theirPawns & file_bb(f)))
{
mgValue[us] -= IsolatedPawnMidgamePenalty[f] / 2;
egValue[us] -= IsolatedPawnEndgamePenalty[f] / 2;
}
}
if (doubled)
{
mgValue[us] -= DoubledPawnMidgamePenalty[f];
egValue[us] -= DoubledPawnEndgamePenalty[f];
}
if (backward)
{
mgValue[us] -= BackwardPawnMidgamePenalty[f];
egValue[us] -= BackwardPawnEndgamePenalty[f];
if (!(theirPawns & file_bb(f)))
{
mgValue[us] -= BackwardPawnMidgamePenalty[f] / 2;
egValue[us] -= BackwardPawnEndgamePenalty[f] / 2;
}
}
if (chain)
{
mgValue[us] += ChainMidgameBonus[f];
egValue[us] += ChainEndgameBonus[f];
}
if (candidate)
{
mgValue[us] += CandidateMidgameBonus[relative_rank(us, s)];
egValue[us] += CandidateEndgameBonus[relative_rank(us, s)];
}
} // while(pawns)
} // for(colors)
pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
// Evaluate pawns for both colors
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
return pi;
}
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) {
Square s;
File f;
Rank r;
bool passed, isolated, doubled, chain, backward, candidate;
int bonus;
Score value = make_score(0, 0);
const Square* ptr = pos.piece_list_begin(Us, PAWN);
// Initialize pawn storm scores by giving bonuses for open files
for (f = FILE_A; f <= FILE_H; f++)
if (!(ourPawns & file_bb(f)))
{
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
pi->qsStormValue[Us] += QStormOpenFileBonus[f];
pi->halfOpenFiles[Us] |= (1 << f);
}
// Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
{
f = square_file(s);
r = square_rank(s);
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
// Passed, isolated or doubled pawn?
passed = Position::pawn_is_passed(theirPawns, Us, s);
isolated = Position::pawn_is_isolated(ourPawns, s);
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
// We calculate kingside and queenside pawn storm
// scores for both colors. These are used when evaluating
// 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
bonus = KStormTable[relative_square(Us, s)];
if (f >= FILE_F)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
while (b)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_H)
bonus += 4*StormLeverBonus[f] - 8*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 4*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 2*square_distance(s, s2);
}
}
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)
{
Square s2 = pop_1st_bit(&b);
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
{
// The enemy pawn has no pawn beside itself, which makes it
// particularly vulnerable. Big bonus, especially against a
// weakness on the rook file.
if (square_file(s2) == FILE_A)
bonus += 4*StormLeverBonus[f] - 16*square_distance(s, s2);
else
bonus += 2*StormLeverBonus[f] - 8*square_distance(s, s2);
} else
// There is at least one enemy pawn beside the enemy pawn we look
// at, which means that the pawn has somewhat better chances of
// defending itself by advancing. Smaller bonus.
bonus += StormLeverBonus[f] - 4*square_distance(s, s2);
}
}
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
//
// If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot
// be backward either.
if ( (passed | isolated | chain)
|| (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{
// We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b = pos.attacks_from<PAWN>(s, Us);
// Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't.
while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8;
// The friendly pawn needs to be at least two ranks closer than the enemy
// pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
}
// Test for candidate passed pawn
candidate = !passed
&& !(theirPawns & file_bb(f))
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns)
- 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
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if (passed && (ourPawns & squares_in_front_of(Us, s)))
passed = false;
// Score this pawn
if (passed)
set_bit(&(pi->passedPawns), s);
if (isolated)
{
value -= IsolatedPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= IsolatedPawnPenalty[f] / 2;
}
if (doubled)
value -= DoubledPawnPenalty[f];
if (backward)
{
value -= BackwardPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= BackwardPawnPenalty[f] / 2;
}
if (chain)
value += ChainBonus[f];
if (candidate)
value += CandidateBonus[relative_rank(Us, s)];
}
return value;
}
/// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {

View File

@@ -47,8 +47,7 @@ class PawnInfo {
public:
PawnInfo() { clear(); }
Value mg_value() const;
Value eg_value() const;
Score pawns_value() const;
Value kingside_storm_value(Color c) const;
Value queenside_storm_value(Color c) const;
Bitboard pawn_attacks(Color c) const;
@@ -65,7 +64,7 @@ private:
Key key;
Bitboard passedPawns;
Bitboard pawnAttacks[2];
int16_t mgValue, egValue;
Score value;
int16_t ksStormValue[2], qsStormValue[2];
uint8_t halfOpenFiles[2];
Square kingSquares[2];
@@ -85,6 +84,9 @@ public:
PawnInfo* get_pawn_info(const Position& pos);
private:
template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
unsigned size;
PawnInfo* entries;
};
@@ -94,12 +96,8 @@ private:
//// Inline functions
////
inline Value PawnInfo::mg_value() const {
return Value(mgValue);
}
inline Value PawnInfo::eg_value() const {
return Value(egValue);
inline Score PawnInfo::pawns_value() const {
return value;
}
inline Bitboard PawnInfo::passed_pawns() const {

View File

@@ -51,18 +51,31 @@ Key Position::zobEp[64];
Key Position::zobCastle[16];
Key Position::zobMaterial[2][8][16];
Key Position::zobSideToMove;
Key Position::zobExclusion;
Value Position::MgPieceSquareTable[16][64];
Value Position::EgPieceSquareTable[16][64];
Score Position::PieceSquareTable[16][64];
static bool RequestPending = false;
////
//// Functions
////
/// Constructors
CheckInfo::CheckInfo(const Position& pos) {
Color us = pos.side_to_move();
Color them = opposite_color(us);
ksq = pos.king_square(them);
dcCandidates = pos.discovered_check_candidates(us);
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
checkSq[KNIGHT] = pos.attacks_from<KNIGHT>(ksq);
checkSq[BISHOP] = pos.attacks_from<BISHOP>(ksq);
checkSq[ROOK] = pos.attacks_from<ROOK>(ksq);
checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK];
checkSq[KING] = EmptyBoardBB;
}
Position::Position(const Position& pos) {
copy(pos);
}
@@ -213,8 +226,7 @@ void Position::from_fen(const string& fen) {
st->key = compute_key();
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
st->mgValue = compute_value<MidGame>();
st->egValue = compute_value<EndGame>();
st->value = compute_value();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
}
@@ -332,16 +344,15 @@ void Position::copy(const Position& pos) {
template<bool FindPinned>
Bitboard Position::hidden_checkers(Color c) const {
Bitboard pinners, result = EmptyBoardBB;
Bitboard result = EmptyBoardBB;
Bitboard pinners = pieces_of_color(FindPinned ? opposite_color(c) : c);
// Pinned pieces protect our king, dicovery checks attack
// the enemy king.
Square ksq = king_square(FindPinned ? c : opposite_color(c));
// Pinners are sliders, not checkers, that give check when
// candidate pinned is removed.
pinners = (pieces(ROOK, QUEEN, FindPinned ? opposite_color(c) : c) & RookPseudoAttacks[ksq])
| (pieces(BISHOP, QUEEN, FindPinned ? opposite_color(c) : c) & BishopPseudoAttacks[ksq]);
// Pinners are sliders, not checkers, that give check when candidate pinned is removed
pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq]) | (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]);
if (FindPinned && pinners)
pinners &= ~st->checkersBB;
@@ -458,19 +469,11 @@ void Position::find_checkers() {
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
bool Position::pl_move_is_legal(Move m) const {
// If we're in check, all pseudo-legal moves are legal, because our
// check evasion generator only generates true legal moves.
return is_check() || pl_move_is_legal(m, pinned_pieces(side_to_move()));
}
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
assert(is_ok());
assert(move_is_ok(m));
assert(pinned == pinned_pieces(side_to_move()));
assert(!is_check());
// Castling moves are checked for legality during move generation.
if (move_is_castle(m))
@@ -482,7 +485,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
assert(color_of_piece_on(from) == us);
assert(piece_on(king_square(us)) == piece_of_color_and_type(us, KING));
// En passant captures are a tricky special case. Because they are
// En passant captures are a tricky special case. Because they are
// rather uncommon, we do it simply by testing whether the king is attacked
// after the move is made
if (move_is_ep(m))
@@ -519,179 +522,142 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
}
/// Position::pl_move_is_evasion() tests whether a pseudo-legal move is a legal evasion
bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
{
assert(is_check());
Color us = side_to_move();
Square from = move_from(m);
Square to = move_to(m);
// King moves and en-passant captures are verified in pl_move_is_legal()
if (type_of_piece_on(from) == KING || move_is_ep(m))
return pl_move_is_legal(m, pinned);
Bitboard target = checkers();
Square checksq = pop_1st_bit(&target);
if (target) // double check ?
return false;
// Our move must be a blocking evasion or a capture of the checking piece
target = squares_between(checksq, king_square(us)) | checkers();
return bit_is_set(target, to) && pl_move_is_legal(m, pinned);
}
/// Position::move_is_check() tests whether a pseudo-legal move is a check
bool Position::move_is_check(Move m) const {
Bitboard dc = discovered_check_candidates(side_to_move());
return move_is_check(m, dc);
return move_is_check(m, CheckInfo(*this));
}
bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
bool Position::move_is_check(Move m, const CheckInfo& ci) const {
assert(is_ok());
assert(move_is_ok(m));
assert(dcCandidates == discovered_check_candidates(side_to_move()));
assert(ci.dcCandidates == discovered_check_candidates(side_to_move()));
assert(color_of_piece_on(move_from(m)) == side_to_move());
assert(piece_on(ci.ksq) == piece_of_color_and_type(opposite_color(side_to_move()), KING));
Color us = side_to_move();
Color them = opposite_color(us);
Square from = move_from(m);
Square to = move_to(m);
Square ksq = king_square(them);
PieceType pt = type_of_piece_on(from);
assert(color_of_piece_on(from) == us);
assert(piece_on(ksq) == piece_of_color_and_type(them, KING));
// Direct check ?
if (bit_is_set(ci.checkSq[pt], to))
return true;
// Proceed according to the type of the moving piece
switch (type_of_piece_on(from))
// Discovery check ?
if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
{
case PAWN:
if (bit_is_set(attacks_from<PAWN>(ksq, them), to)) // Normal check?
// For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING)
||(direction_between_squares(from, ci.ksq) != direction_between_squares(to, ci.ksq)))
return true;
if ( dcCandidates // Discovered check?
&& bit_is_set(dcCandidates, from)
&& (direction_between_squares(from, ksq) != direction_between_squares(to, ksq)))
return true;
if (move_is_promotion(m)) // Promotion with check?
{
Bitboard b = occupied_squares();
clear_bit(&b, from);
switch (move_promotion_piece(m))
{
case KNIGHT:
return bit_is_set(attacks_from<KNIGHT>(to), ksq);
case BISHOP:
return bit_is_set(bishop_attacks_bb(to, b), ksq);
case ROOK:
return bit_is_set(rook_attacks_bb(to, b), ksq);
case QUEEN:
return bit_is_set(queen_attacks_bb(to, b), ksq);
default:
assert(false);
}
}
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through the
// captured pawn.
else if (move_is_ep(m))
{
Square capsq = make_square(square_file(to), square_rank(from));
Bitboard b = occupied_squares();
clear_bit(&b, from);
clear_bit(&b, capsq);
set_bit(&b, to);
return (rook_attacks_bb(ksq, b) & pieces(ROOK, QUEEN, us))
||(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, us));
}
return false;
// Test discovered check and normal check according to piece type
case KNIGHT:
return (dcCandidates && bit_is_set(dcCandidates, from))
|| bit_is_set(attacks_from<KNIGHT>(ksq), to);
case BISHOP:
return (dcCandidates && bit_is_set(dcCandidates, from))
|| (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from<BISHOP>(ksq), to));
case ROOK:
return (dcCandidates && bit_is_set(dcCandidates, from))
|| (direction_is_straight(ksq, to) && bit_is_set(attacks_from<ROOK>(ksq), to));
case QUEEN:
// Discovered checks are impossible!
assert(!bit_is_set(dcCandidates, from));
return ( (direction_is_straight(ksq, to) && bit_is_set(attacks_from<ROOK>(ksq), to))
|| (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from<BISHOP>(ksq), to)));
case KING:
// Discovered check?
if ( bit_is_set(dcCandidates, from)
&& (direction_between_squares(from, ksq) != direction_between_squares(to, ksq)))
return true;
// Castling with check?
if (move_is_castle(m))
{
Square kfrom, kto, rfrom, rto;
Bitboard b = occupied_squares();
kfrom = from;
rfrom = to;
if (rfrom > kfrom)
{
kto = relative_square(us, SQ_G1);
rto = relative_square(us, SQ_F1);
} else {
kto = relative_square(us, SQ_C1);
rto = relative_square(us, SQ_D1);
}
clear_bit(&b, kfrom);
clear_bit(&b, rfrom);
set_bit(&b, rto);
set_bit(&b, kto);
return bit_is_set(rook_attacks_bb(rto, b), ksq);
}
return false;
default: // NO_PIECE_TYPE
break;
}
assert(false);
// Can we skip the ugly special cases ?
if (!move_is_special(m))
return false;
Color us = side_to_move();
Bitboard b = occupied_squares();
// Promotion with check ?
if (move_is_promotion(m))
{
clear_bit(&b, from);
switch (move_promotion_piece(m))
{
case KNIGHT:
return bit_is_set(attacks_from<KNIGHT>(to), ci.ksq);
case BISHOP:
return bit_is_set(bishop_attacks_bb(to, b), ci.ksq);
case ROOK:
return bit_is_set(rook_attacks_bb(to, b), ci.ksq);
case QUEEN:
return bit_is_set(queen_attacks_bb(to, b), ci.ksq);
default:
assert(false);
}
}
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through the
// captured pawn.
if (move_is_ep(m))
{
Square capsq = make_square(square_file(to), square_rank(from));
clear_bit(&b, from);
clear_bit(&b, capsq);
set_bit(&b, to);
return (rook_attacks_bb(ci.ksq, b) & pieces(ROOK, QUEEN, us))
||(bishop_attacks_bb(ci.ksq, b) & pieces(BISHOP, QUEEN, us));
}
// Castling with check ?
if (move_is_castle(m))
{
Square kfrom, kto, rfrom, rto;
kfrom = from;
rfrom = to;
if (rfrom > kfrom)
{
kto = relative_square(us, SQ_G1);
rto = relative_square(us, SQ_F1);
} else {
kto = relative_square(us, SQ_C1);
rto = relative_square(us, SQ_D1);
}
clear_bit(&b, kfrom);
clear_bit(&b, rfrom);
set_bit(&b, rto);
set_bit(&b, kto);
return bit_is_set(rook_attacks_bb(rto, b), ci.ksq);
}
return false;
}
/// Position::update_checkers() udpates chekers info given the move. It is called
/// in do_move() and is faster then find_checkers().
template<PieceType Piece>
inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square from,
Square to, Bitboard dcCandidates) {
const bool Bishop = (Piece == QUEEN || Piece == BISHOP);
const bool Rook = (Piece == QUEEN || Piece == ROOK);
const bool Slider = Bishop || Rook;
// Direct checks
if ( ( (Bishop && bit_is_set(BishopPseudoAttacks[ksq], to))
|| (Rook && bit_is_set(RookPseudoAttacks[ksq], to)))
&& bit_is_set(attacks_from<Piece>(ksq), to)) // slow, try to early skip
set_bit(pCheckersBB, to);
else if ( Piece != KING
&& !Slider
&& bit_is_set(Piece == PAWN ? attacks_from<PAWN>(ksq, opposite_color(sideToMove))
: attacks_from<Piece>(ksq), to))
set_bit(pCheckersBB, to);
// Discovery checks
if (Piece != QUEEN && bit_is_set(dcCandidates, from))
{
if (Piece != ROOK)
(*pCheckersBB) |= (attacks_from<ROOK>(ksq) & pieces(ROOK, QUEEN, side_to_move()));
if (Piece != BISHOP)
(*pCheckersBB) |= (attacks_from<BISHOP>(ksq) & pieces(BISHOP, QUEEN, side_to_move()));
}
}
/// Position::do_move() makes a move, and saves all information necessary
/// to a StateInfo object. The move is assumed to be legal.
/// Pseudo-legal moves should be filtered out before this function is called.
void Position::do_move(Move m, StateInfo& newSt) {
do_move(m, newSt, discovered_check_candidates(side_to_move()));
CheckInfo ci(*this);
do_move(m, newSt, ci, move_is_check(m, ci));
}
void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
assert(is_ok());
assert(move_is_ok(m));
@@ -702,10 +668,10 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
// ones which are recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state.
struct ReducedStateInfo {
Key key, pawnKey, materialKey;
int castleRights, rule50;
Key pawnKey, materialKey;
int castleRights, rule50, pliesFromNull;
Square epSquare;
Value mgValue, egValue;
Score value;
Value npMaterial[2];
};
@@ -724,6 +690,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
// Increment the 50 moves rule draw counter. Resetting it to zero in the
// case of non-reversible moves is taken care of later.
st->rule50++;
st->pliesFromNull++;
if (move_is_castle(m))
{
@@ -741,16 +708,15 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
Piece piece = piece_on(from);
PieceType pt = type_of_piece(piece);
PieceType capture = ep ? PAWN : type_of_piece_on(to);
assert(color_of_piece_on(from) == us);
assert(color_of_piece_on(to) == them || square_is_empty(to));
assert(!(ep || pm) || piece == piece_of_color_and_type(us, PAWN));
assert(!pm || relative_rank(us, to) == RANK_8);
st->capture = ep ? PAWN : type_of_piece_on(to);
if (st->capture)
do_capture_move(key, st->capture, them, to, ep);
if (capture)
do_capture_move(key, capture, them, to, ep);
// Update hash key
key ^= zobrist[us][pt][from] ^ zobrist[us][pt][to];
@@ -800,7 +766,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
// Set en passant square, only if moved pawn can be captured
if (abs(int(to) - int(from)) == 16)
if ((to ^ from) == 16)
{
if (attacks_from<PAWN>(from + (us == WHITE ? DELTA_N : DELTA_S), us) & pieces(PAWN, them))
{
@@ -811,8 +777,10 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
}
// Update incremental scores
st->mgValue += pst_delta<MidGame>(piece, from, to);
st->egValue += pst_delta<EndGame>(piece, from, to);
st->value += pst_delta(piece, from, to);
// Set capture piece
st->capture = capture;
if (pm) // promotion ?
{
@@ -847,10 +815,8 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
st->pawnKey ^= zobrist[us][PAWN][to];
// Partially revert and update incremental scores
st->mgValue -= pst<MidGame>(us, PAWN, to);
st->mgValue += pst<MidGame>(us, promotion, to);
st->egValue -= pst<EndGame>(us, PAWN, to);
st->egValue += pst<EndGame>(us, promotion, to);
st->value -= pst(us, PAWN, to);
st->value += pst(us, promotion, to);
// Update material
st->npMaterial[us] += piece_value_midgame(promotion);
@@ -860,29 +826,33 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
st->key = key;
// Update checkers bitboard, piece must be already moved
if (ep | pm)
st->checkersBB = attackers_to(king_square(them)) & pieces_of_color(us);
else
st->checkersBB = EmptyBoardBB;
if (moveIsCheck)
{
st->checkersBB = EmptyBoardBB;
Square ksq = king_square(them);
switch (pt)
if (ep | pm)
st->checkersBB = attackers_to(king_square(them)) & pieces_of_color(us);
else
{
case PAWN: update_checkers<PAWN>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
case KNIGHT: update_checkers<KNIGHT>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
case BISHOP: update_checkers<BISHOP>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
case ROOK: update_checkers<ROOK>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
case QUEEN: update_checkers<QUEEN>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
case KING: update_checkers<KING>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
default: assert(false); break;
// Direct checks
if (bit_is_set(ci.checkSq[pt], to))
st->checkersBB = SetMaskBB[to];
// Discovery checks
if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
{
if (pt != ROOK)
st->checkersBB |= (attacks_from<ROOK>(ci.ksq) & pieces(ROOK, QUEEN, us));
if (pt != BISHOP)
st->checkersBB |= (attacks_from<BISHOP>(ci.ksq) & pieces(BISHOP, QUEEN, us));
}
}
}
// Finish
sideToMove = opposite_color(sideToMove);
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
assert(is_ok());
}
@@ -918,8 +888,7 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ
key ^= zobrist[them][capture][capsq];
// Update incremental scores
st->mgValue -= pst<MidGame>(them, capture, capsq);
st->egValue -= pst<EndGame>(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.
@@ -1000,7 +969,7 @@ void Position::do_castle_move(Move m) {
set_bit(&(byColorBB[us]), rto);
set_bit(&(byTypeBB[ROOK]), rto);
set_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
// Update board array
Piece king = piece_of_color_and_type(us, KING);
Piece rook = piece_of_color_and_type(us, ROOK);
@@ -1016,10 +985,8 @@ void Position::do_castle_move(Move m) {
index[rto] = tmp;
// Update incremental scores
st->mgValue += pst_delta<MidGame>(king, kfrom, kto);
st->egValue += pst_delta<EndGame>(king, kfrom, kto);
st->mgValue += pst_delta<MidGame>(rook, rfrom, rto);
st->egValue += pst_delta<EndGame>(rook, rfrom, rto);
st->value += pst_delta(king, kfrom, kto);
st->value += pst_delta(rook, rfrom, rto);
// Update hash key
st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
@@ -1045,9 +1012,7 @@ void Position::do_castle_move(Move m) {
// Finish
sideToMove = opposite_color(sideToMove);
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
assert(is_ok());
}
@@ -1189,7 +1154,7 @@ void Position::undo_castle_move(Move m) {
assert(piece_on(kto) == piece_of_color_and_type(us, KING));
assert(piece_on(rto) == piece_of_color_and_type(us, ROOK));
// Remove pieces from destination squares:
clear_bit(&(byColorBB[us]), kto);
clear_bit(&(byTypeBB[KING]), kto);
@@ -1197,7 +1162,7 @@ void Position::undo_castle_move(Move m) {
clear_bit(&(byColorBB[us]), rto);
clear_bit(&(byTypeBB[ROOK]), rto);
clear_bit(&(byTypeBB[0]), rto); // HACK: byTypeBB[0] == occupied squares
// Put pieces on source squares:
set_bit(&(byColorBB[us]), kfrom);
set_bit(&(byTypeBB[KING]), kfrom);
@@ -1239,9 +1204,9 @@ void Position::do_null_move(StateInfo& backupSt) {
// a backup storage not as a new state to be used.
backupSt.key = st->key;
backupSt.epSquare = st->epSquare;
backupSt.mgValue = st->mgValue;
backupSt.egValue = st->egValue;
backupSt.value = st->value;
backupSt.previous = st->previous;
backupSt.pliesFromNull = st->pliesFromNull;
st->previous = &backupSt;
// Save the current key to the history[] array, in order to be able to
@@ -1258,10 +1223,9 @@ void Position::do_null_move(StateInfo& backupSt) {
sideToMove = opposite_color(sideToMove);
st->epSquare = SQ_NONE;
st->rule50++;
st->pliesFromNull = 0;
st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue;
gamePly++;
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
}
@@ -1276,9 +1240,9 @@ void Position::undo_null_move() {
StateInfo* backupSt = st->previous;
st->key = backupSt->key;
st->epSquare = backupSt->epSquare;
st->mgValue = backupSt->mgValue;
st->egValue = backupSt->egValue;
st->value = backupSt->value;
st->previous = backupSt->previous;
st->pliesFromNull = backupSt->pliesFromNull;
// Update the necessary information
sideToMove = opposite_color(sideToMove);
@@ -1346,6 +1310,10 @@ int Position::see(Square from, Square to) const {
Piece capture = piece_on(to);
Bitboard occ = occupied_squares();
// King cannot be recaptured
if (type_of_piece(piece) == KING)
return seeValues[capture];
// Handle en passant moves
if (st->epSquare == to && type_of_piece_on(from) == PAWN)
{
@@ -1484,8 +1452,8 @@ void Position::clear() {
for (int i = 0; i < 64; i++)
board[i] = EMPTY;
for (int i = 0; i < 7; i++)
for (int j = 0; j < 8; j++)
for (int i = 0; i < 8; i++)
for (int j = 0; j < 16; j++)
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
sideToMove = WHITE;
@@ -1619,10 +1587,9 @@ Key Position::compute_material_key() const {
/// game and the endgame. These functions are used to initialize the incremental
/// scores when a new position is set up, and to verify that the scores are correctly
/// updated by do_move and undo_move when the program is running in debug mode.
template<Position::GamePhase Phase>
Value Position::compute_value() const {
Score Position::compute_value() const {
Value result = Value(0);
Score result = make_score(0, 0);
Bitboard b;
Square s;
@@ -1634,12 +1601,11 @@ Value Position::compute_value() const {
{
s = pop_1st_bit(&b);
assert(piece_on(s) == piece_of_color_and_type(c, pt));
result += pst<Phase>(c, pt, s);
result += pst(c, pt, s);
}
}
const Value TempoValue = (Phase == MidGame ? TempoValueMidgame : TempoValueEndgame);
result += (side_to_move() == WHITE)? TempoValue / 2 : -TempoValue / 2;
result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2);
return result;
}
@@ -1683,7 +1649,7 @@ bool Position::is_draw() const {
return true;
// Draw by repetition?
for (int i = 2; i < Min(gamePly, st->rule50); i += 2)
for (int i = 2; i < Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2)
if (history[gamePly - i] == st->key)
return true;
@@ -1697,8 +1663,7 @@ bool Position::is_draw() const {
bool Position::is_mate() const {
MoveStack moves[256];
return is_check() && (generate_evasions(*this, moves, pinned_pieces(sideToMove)) == moves);
return is_check() && (generate_moves(*this, moves, false) == moves);
}
@@ -1719,11 +1684,10 @@ bool Position::has_mate_threat(Color c) {
MoveStack mlist[120];
bool result = false;
Bitboard dc = discovered_check_candidates(sideToMove);
Bitboard pinned = pinned_pieces(sideToMove);
// Generate pseudo-legal non-capture and capture check moves
MoveStack* last = generate_non_capture_checks(*this, mlist, dc);
MoveStack* last = generate_non_capture_checks(*this, mlist);
last = generate_captures(*this, last);
// Loop through the moves, and see if one of them is mate
@@ -1773,6 +1737,8 @@ void Position::init_zobrist() {
for (int i = 0; i < 16; i++)
zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
zobExclusion = genrand_int64();
}
@@ -1790,16 +1756,12 @@ void Position::init_piece_square_tables() {
for (Piece p = WP; p <= WK; p++)
{
i = (r == 0)? 0 : (genrand_int32() % (r*2) - r);
MgPieceSquareTable[p][s] = Value(MgPST[p][s] + i);
EgPieceSquareTable[p][s] = Value(EgPST[p][s] + i);
PieceSquareTable[p][s] = make_score(MgPST[p][s] + i, EgPST[p][s] + i);
}
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (Piece p = BP; p <= BK; p++)
{
MgPieceSquareTable[p][s] = -MgPieceSquareTable[p-8][flip_square(s)];
EgPieceSquareTable[p][s] = -EgPieceSquareTable[p-8][flip_square(s)];
}
PieceSquareTable[p][s] = -PieceSquareTable[p-8][flip_square(s)];
}
@@ -1854,8 +1816,7 @@ void Position::flipped_copy(const Position& pos) {
st->materialKey = compute_material_key();
// Incremental scores
st->mgValue = compute_value<MidGame>();
st->egValue = compute_value<EndGame>();
st->value = compute_value();
// Material
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
@@ -1982,14 +1943,8 @@ bool Position::is_ok(int* failedStep) const {
// Incremental eval OK?
if (failedStep) (*failedStep)++;
if (debugIncrementalEval)
{
if (st->mgValue != compute_value<MidGame>())
return false;
if (st->egValue != compute_value<EndGame>())
return false;
}
if (debugIncrementalEval && st->value != compute_value())
return false;
// Non-pawn material OK?
if (failedStep) (*failedStep)++;

View File

@@ -63,6 +63,18 @@ const int MaxGameLength = 220;
//// Types
////
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo {
CheckInfo(const Position&);
Square ksq;
Bitboard dcCandidates;
Bitboard checkSq[8];
};
/// Castle rights, encoded as bit fields
enum CastleRights {
@@ -87,12 +99,13 @@ enum Phase {
/// must be passed as a parameter.
struct StateInfo {
Key key, pawnKey, materialKey;
int castleRights, rule50;
Key pawnKey, materialKey;
int castleRights, rule50, pliesFromNull;
Square epSquare;
Value mgValue, egValue;
Score value;
Value npMaterial[2];
Key key;
PieceType capture;
Bitboard checkersBB;
StateInfo* previous;
@@ -202,11 +215,12 @@ public:
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves
bool pl_move_is_legal(Move m) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool move_is_check(Move m) const;
bool move_is_check(Move m, Bitboard dcCandidates) const;
bool move_is_check(Move m, const CheckInfo& ci) const;
bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const;
@@ -222,7 +236,7 @@ public:
// Doing and undoing moves
void saveState();
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, Bitboard dcCandidates);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
void do_null_move(StateInfo& st);
void undo_null_move();
@@ -235,15 +249,14 @@ public:
// Accessing hash keys
Key get_key() const;
Key get_exclusion_key() const;
Key get_pawn_key() const;
Key get_material_key() const;
// Incremental evaluation
Value mg_value() const;
Value eg_value() const;
Score value() const;
Value non_pawn_material(Color c) const;
Phase game_phase() const;
template<GamePhase> Value pst_delta(Piece piece, Square from, Square to) const;
Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks
bool is_mate() const;
@@ -283,9 +296,6 @@ private:
void undo_castle_move(Move m);
void find_checkers();
template<PieceType Piece>
void update_checkers(Bitboard* pCheckersBB, Square ksq, Square from, Square to, Bitboard dcCandidates);
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
@@ -295,8 +305,8 @@ private:
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
template<GamePhase> Value pst(Color c, PieceType pt, Square s) const;
template<GamePhase> Value compute_value() const;
Score pst(Color c, PieceType pt, Square s) const;
Score compute_value() const;
Value compute_non_pawn_material(Color c) const;
// Board
@@ -327,8 +337,8 @@ private:
static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove;
static Value MgPieceSquareTable[16][64];
static Value EgPieceSquareTable[16][64];
static Score PieceSquareTable[16][64];
static Key zobExclusion;
};
@@ -493,6 +503,10 @@ inline Key Position::get_key() const {
return st->key;
}
inline Key Position::get_exclusion_key() const {
return st->key ^ zobExclusion;
}
inline Key Position::get_pawn_key() const {
return st->pawnKey;
}
@@ -501,46 +515,22 @@ inline Key Position::get_material_key() const {
return st->materialKey;
}
template<Position::GamePhase Ph>
inline Value Position::pst(Color c, PieceType pt, Square s) const {
return (Ph == MidGame ? MgPieceSquareTable[piece_of_color_and_type(c, pt)][s]
: EgPieceSquareTable[piece_of_color_and_type(c, pt)][s]);
inline Score Position::pst(Color c, PieceType pt, Square s) const {
return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
}
template<Position::GamePhase Ph>
inline Value Position::pst_delta(Piece piece, Square from, Square to) const {
return (Ph == MidGame ? MgPieceSquareTable[piece][to] - MgPieceSquareTable[piece][from]
: EgPieceSquareTable[piece][to] - EgPieceSquareTable[piece][from]);
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
}
inline Value Position::mg_value() const {
return st->mgValue;
}
inline Value Position::eg_value() const {
return st->egValue;
inline Score Position::value() const {
return st->value;
}
inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c];
}
inline Phase Position::game_phase() const {
// Values modified by Joona Kiiski
static const Value MidgameLimit = Value(15581);
static const Value EndgameLimit = Value(3998);
Value npm = non_pawn_material(WHITE) + non_pawn_material(BLACK);
if (npm >= MidgameLimit)
return PHASE_MIDGAME;
else if(npm <= EndgameLimit)
return PHASE_ENDGAME;
else
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
inline bool Position::move_is_passed_pawn_push(Move m) const {
Color c = side_to_move();
@@ -568,8 +558,13 @@ inline bool Position::has_pawn_on_7th(Color c) const {
inline bool Position::move_is_capture(Move m) const {
// Move must not be MOVE_NONE !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
}
return (!square_is_empty(move_to(m)) && !move_is_castle(m)) || move_is_ep(m);
inline bool Position::move_is_capture_or_promotion(Move m) const {
// Move must not be MOVE_NONE !
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
}
#endif // !defined(POSITION_H_INCLUDED)

View File

@@ -142,13 +142,14 @@ Move move_from_san(const Position& pos, const string& movestr) {
assert(pos.is_ok());
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
// Castling moves
if (movestr == "O-O-O" || movestr == "O-O-O+")
{
Move m;
while ((m = mp.get_next_move()) != MOVE_NONE)
if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned))
return m;
return MOVE_NONE;
@@ -157,7 +158,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
{
Move m;
while ((m = mp.get_next_move()) != MOVE_NONE)
if (move_is_short_castle(m) && pos.pl_move_is_legal(m))
if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned))
return m;
return MOVE_NONE;
@@ -367,11 +368,12 @@ namespace {
return AMBIGUITY_NONE;
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
Move mv, moveList[8];
int n = 0;
while ((mv = mp.get_next_move()) != MOVE_NONE)
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned))
moveList[n++] = mv;
if (n == 1)

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@ const int KILLER_MAX = 2;
/// current ply.
struct SearchStack {
Move pv[PLY_MAX];
Move pv[PLY_MAX_PLUS_2];
Move currentMove;
Move mateKiller;
Move threatMove;
@@ -69,6 +69,7 @@ extern void stop_threads();
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
int time[], int increment[], int movesToGo, int maxDepth,
int maxNodes, int maxTime, Move searchMoves[]);
extern int perft(Position &pos, Depth depth);
extern int64_t nodes_searched();

View File

@@ -46,13 +46,13 @@ const int THREAD_MAX = 8;
struct SplitPoint {
SplitPoint *parent;
Position pos;
SearchStack sstack[THREAD_MAX][PLY_MAX];
SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
SearchStack *parentSstack;
int ply;
Depth depth;
volatile Value alpha, beta, bestValue;
volatile Value alpha, beta, bestValue, futilityValue;
Value approximateEval;
bool pvNode;
Bitboard dcCandidates;
int master, slaves[THREAD_MAX];
Lock lock;
MovePicker *mp;

View File

@@ -208,7 +208,9 @@ void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
for (int i = 0; pv[i] != MOVE_NONE; i++)
{
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i]);
TTEntry *tte = retrieve(p.get_key());
if (!tte || tte->move() != pv[i])
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i]);
p.do_move(pv[i], st);
}
}
@@ -220,7 +222,7 @@ void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
/// will often get single-move PVs when the search stops while failing high,
/// and a single-move PV means that we don't have a ponder move.
void TranspositionTable::extract_pv(const Position& pos, Move pv[], int pvSize) {
void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PLY_MAX) {
const TTEntry* tte;
StateInfo st;
@@ -236,7 +238,7 @@ void TranspositionTable::extract_pv(const Position& pos, Move pv[], int pvSize)
&& tte->move() != MOVE_NONE
&& move_is_legal(p, tte->move())
&& (!p.is_draw() || ply < 2)
&& ply < pvSize)
&& ply < PLY_MAX)
{
pv[ply] = tte->move();
p.do_move(pv[ply++], st);

View File

@@ -102,7 +102,7 @@ public:
void prefetch(const Key posKey) const;
void new_search();
void insert_pv(const Position& pos, Move pv[]);
void extract_pv(const Position& pos, Move pv[], int pvSize);
void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
int full() const;
private:

View File

@@ -33,7 +33,7 @@ typedef __int16 int16;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef __int16 int16_t;

View File

@@ -61,6 +61,7 @@ namespace {
void set_option(UCIInputParser& uip);
void set_position(UCIInputParser& uip);
bool go(UCIInputParser& uip);
void perft(UCIInputParser& uip);
}
@@ -123,7 +124,7 @@ namespace {
}
else if (token == "ucinewgame")
{
push_button("Clear Hash");
push_button("New Game");
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
@@ -147,14 +148,16 @@ namespace {
else if (token == "eval")
{
EvalInfo ei;
cout << "Incremental mg: " << RootPosition.mg_value()
<< "\nIncremental eg: " << RootPosition.eg_value()
cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
}
else if (token == "key")
cout << "key: " << hex << RootPosition.get_key()
<< "\nmaterial key: " << RootPosition.get_material_key()
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
else if (token == "perft")
perft(uip);
else
{
cout << "Unknown command: " << command << endl;
@@ -318,4 +321,23 @@ namespace {
time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
}
void perft(UCIInputParser& uip) {
string token;
int depth, tm, n;
Position pos = RootPosition;
if (uip.eof())
return;
uip >> depth;
tm = get_system_time();
n = perft(pos, depth * OnePly);
tm = get_system_time() - tm;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << tm
<< "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
}
}

View File

@@ -125,6 +125,7 @@ namespace {
o["Threads"] = Option(1, 1, 8);
o["Hash"] = Option(32, 4, 4096);
o["Clear Hash"] = Option(false, BUTTON);
o["New Game"] = Option(false, BUTTON);
o["Ponder"] = Option(true);
o["OwnBook"] = Option(true);
o["MultiPV"] = Option(1, 1, 500);
@@ -169,6 +170,18 @@ namespace {
return ret;
}
// Specialization for std::string where instruction 'ss >> ret;'
// would erroneusly tokenize a string with spaces.
template<>
string get_option_value<string>(const string& optionName) {
if (options.find(optionName) == options.end())
return string();
return options[optionName].currentValue;
}
}
////

View File

@@ -52,6 +52,32 @@ enum Value {
};
/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value.
enum Score {};
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
inline Value mg_value(Score s) { return Value((int(s) + 32768) >> 16); }
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
inline Score operator-(Score s) { return Score(-int(s)); }
inline Score operator+(Score s1, Score s2) { return Score(int(s1) + int(s2)); }
inline Score operator-(Score s1, Score s2) { return Score(int(s1) - int(s2)); }
inline void operator+=(Score& s1, Score s2) { s1 = Score(int(s1) + int(s2)); }
inline void operator-=(Score& s1, Score s2) { s1 = Score(int(s1) - int(s2)); }
inline Score operator*(int i, Score s) { return Score(i * int(s)); }
// Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
////
//// Constants and variables
////
@@ -97,8 +123,7 @@ const Value PieceValueEndgame[17] = {
/// Bonus for having the side to move (modified by Joona Kiiski)
const Value TempoValueMidgame = Value(48);
const Value TempoValueEndgame = Value(22);
const Score TempoValue = make_score(48, 22);
////