Compare commits

...

580 Commits

Author SHA1 Message Date
Marco Costalba aa2368a687 Stockfish 3
Stockfish bench signature is: 4176431
2013-04-30 19:42:43 +02:00
Marco Costalba 293c44bc09 Revert "Cache line aligned TT"
This reverts commit 083fe58124

It seems to break Android build

No functional change.
2013-04-30 19:42:21 +02:00
Marco Costalba 06b9140e5c Temporary revert "Expose EvalInfo struct to search"
It is not needed for the release and introduces
a slowdown, although very small.

Probably it will be readded after the release.

No functional change.
2013-04-29 00:55:32 +02:00
Marco Costalba 156635749b Fix a 'value > VALUE_INFINITE' assert
This fixes an assert while testing with debug on.

Assert was due to static null pruning returning value

eval - futility_margin(depth, (ss-1)->futilityMoveCount)

That was sometimes higher than VALUE_INFINITE triggering
an assert at the caller site.

Because eval con be equal to ttValue and anyhow is read from
TT that can be corrupted in SMP case, we need to sanity
check it before to use.

bench: 4176431
2013-04-27 13:08:11 +02:00
Marco Costalba 083fe58124 Cache line aligned TT
Let TT clusters (16*4=64 bytes) to hold on a singe cache line.
This avoids the need for the double prefetch.

Original patches by Lucas and Jean-Francois that has also tested
on his AMD FX:

BIG HASHTABLE

./stockfish bench 1024 1 18 > /dev/null

Before:
1437642 nps
1426519 nps
1438493 nps

After:
1474482 nps
1476375 nps
1475877 nps

SMALL HASHTABLE

./stockfish bench 128 1 18 > /dev/null

Before:
1435207 nps
1435586 nps
1433741 nps

After:
1479143 nps
1471042 nps
1472286 nps

No functional change.
2013-04-26 19:38:11 +02:00
Marco Costalba e508494a99 Fix a crash introduced few days ago
Crash is due to uninitialized ss->futilityMoveCount that
when happens to be negative, yields to an out of range
access in futility_margin().

Bug is subtle because it shows itself only in SMP case.
Indeed in single thread mode we only use the

Stack ss[MAX_PLY_PLUS_2];

Allocated at the begin of id_loop() and due to pure
(bad) luck, it happens that for all the MAX_PLY_PLUS_2
elements, ss[i].futilityMoveCount >= 0

Note that the patch does not prevent futilityMoveCount
to be overwritten after, for instance singular search
or null verification, but to keep things readable and
because the effect is almost unmeasurable, we here
prefer a slightly incorrect but simpler patch.

bench: 4311634
2013-04-26 12:14:01 +02:00
Marco Costalba 2ef53ee368 Store Eval::Info in Search::Stack
Instead of a pointer. This should fix the issue of
remaining with a stale pointer when for instance calling
IID, but also null search verification, singular search
and razoring where we call search with the same ss
pointer. In this case ss->ei is overwritten in the
search() call and upon returning remains stale.

This patch could have a performance hit because Eval::Info
is big (176 bytes) and during splitting we copy 4 ss entries.

On the good side, this patch is a clean solution.

Proposed by Gary.

No functional change.
2013-04-25 21:52:26 +02:00
Marco Costalba d810441b35 Expose EvalInfo struct to search
Allow to use EvalInfo struct, populated by
evaluation(), in search.

In particular we allocate Eval::Info on the stack
and pass a pointer to this to evaluate().

Also add to Search::Stack a pointer to Eval::Info,
this allows to reference eval info of previous/next
nodes.

WARNING: Eval::Info is NOT initialized and is populated
by evaluate(), only if the latter is called, and this
does not happen in all the code paths, so care should be
taken when accessing this struct.

No functional change.
2013-04-25 12:57:37 +02:00
Ryan Schmitt 79bcb2ca54 Increase rook/queen on 7th bonus
Shows an increase at 15+0.05
LLR: 3.01 (-2.94,2.94)
Total: 20450 W: 4091 L: 3927 D: 12432

And at 60+0.05
LLR: 2.97 (-2.94,2.94)
Total: 61432 W: 10849 L: 10441 D: 40142

bench: 4493356

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-04-25 12:13:17 +02:00
Marco Costalba 289a767ab3 Merge Joona's increased static null pruning
The idea is to fail high more easily in static
null test if in the parent node we are already
very deep in the move list, so the propability
to fail high there is very low.

[edit: I have slightly changed the functionality
moving

ss->futilityMoveCount = moveCount;

At the end of the pruning code, this should not affect
ELO in anyway, but makes code more natural and logic]

Test with SPRT is good at 15+0.05
LLR: 2.96 (-2.94,2.94)
Total: 50653 W: 10024 L: 9780 D: 30849

And at 60+0.05
LLR: 2.97 (-2.94,2.94)
Total: 40799 W: 7227 L: 6921 D: 26651

bench: 4530093
2013-04-25 12:05:00 +02:00
Marco Costalba 4381ea23fe Fix cpu_count() on some platforms
When we use sysconf(_SC_NPROCESSORS_ONLN) to get number of
cores, we have to include sysconf library that is unistd.h

Sometimes it happens to work just becuase unistd.h indirectly
included by some other libraries, but not always.

Reported and fixed by Eyal BD

No functional change.
2013-04-25 10:56:56 +02:00
Joona Kiiski 817ca1820b Fix potential overflow 2013-04-23 07:26:36 +01:00
Joona Kiiski 8df17204f4 More aggressive post-futility pruning 2013-04-21 14:53:27 +01:00
Marco Costalba f84f04742a Skip a couple of popcount in previous patch
And some little tidy up

No functional change.
2013-04-19 10:31:18 +02:00
Marco Costalba cc40d1c46a Merge Joona's bishop+pawn tweak
The idea is to penalize a bishop in case of
its pawns are on the same colored squares.

Good at short 15"+0.05 TC
LLR: 2.95 (-2.94,2.94)
Total: 4252 W: 925 L: 806 D: 2521

And at longer 60"+0.05 TC
LLR: 2.95 (-2.94,2.94)
Total: 15006 W: 2743 L: 2564 D: 9699

bench: 5274705
2013-04-19 09:36:00 +02:00
Joona Kiiski 818e0b2d2b Try more aggressive version 2013-04-17 07:58:59 +01:00
Joona Kiiski 3e4dfb49a7 Give a small penalty for bishop for each pawn on the same colored square 2013-04-16 21:20:31 +01:00
Marco Costalba 87436e5544 Skip a redundant check
Spotted by Joona

No functional change.
2013-04-14 23:29:25 +02:00
Marco Costalba fe72c93141 De-templetize Position::is_draw()
Now that we always check for repetition we don't
need a template anymore.

No functional change.
2013-04-10 22:23:48 +02:00
Joona Kiiski 75221fcf5e Always check repetition
It seems stronger both at fast 15+0.05 TC with fixed game number test:
ELO: 2.74 +-2.7 (95%) LOS: 97.6%
Total: 24000 W: 4698 L: 4509 D: 14793

And also at long 60+0.05 TC with SPRT
LLR: 3.05 (-2.94,2.94)
Total: 38986 W: 6845 L: 6547 D: 25594

bench: 5157061
2013-04-10 22:20:40 +02:00
Marco Costalba a95cbca568 Simplify and speed up previous patch
Use an optinal argument instead of a template
parameter. Interestingly, not only is simpler,
but also faster, perhaps due to less L1 instruction
cache pressure because we don't duplicate the very
used SEE code path.

No functional change.
2013-04-09 23:32:06 +02:00
Joona Kiiski d23454854e Document asymmetric SEE pruning trick
Here are the tests:

sprt @ 60+0.05
ELO: 3.53 +-2.8 (95%) LOS: 99.3%
Total: 18794 W: 3098 L: 2907 D: 12789

16000 @ 60+0.05
ELO: 1.39 +-3.1 (95%) LOS: 81.0%
Total: 16000 W: 2689 L: 2625 D: 10686

16000 @ 15+0.05
ELO: 2.82 +-3.3 (95%) LOS: 95.1%
Total: 16000 W: 3148 L: 3018 D: 9834

No functional change

Signature: 4969307
2013-04-09 23:31:57 +02:00
Joona Kiiski c2902112e5 Don't treat king safety differently in AnalysisMode
Rationale:

- Current settings seem to make engine *significantly* weaker in analysis mode.
- In practice this setting only has effect when king safety scores are high.
- Even in analysis mode its far more important to know if one side is getting mated,
rather than get evaluation correct with 1cp accuracy.

No functional change
2013-04-09 23:29:58 +02:00
Marco Costalba 9498b2af82 Rescale UCI parameters to 100
And correspondingly modify internal ones
to compensate it.

No functional change.
2013-04-09 23:29:58 +02:00
Marco Costalba 2a5ae34bb2 Tweak some UCI parameters
According to Jean-Paul this setup should be stronger
than default.

And SPRT test seems to confirm it:

At fast TC 15"+0.05
ELO: 3.33 +-2.7 (95%) LOS: 99.2%
Total: 25866 W: 5461 L: 5213 D: 15192

At longer TC 60"+0.05
ELO: 7.27 +-5.0 (95%) LOS: 99.8%
Total: 6544 W: 1212 L: 1075 D: 4257

bench: 5473339

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-04-09 23:29:58 +02:00
Marco Costalba d3fe153fe6 Re-add 'Cowardice' and 'Aggressiveness' UCI options
I have lost my bet with Jean-Paul, so now I re-add
the two options...and I am glad of it :-)

No functional change.
2013-04-09 23:29:58 +02:00
Marco Costalba 889922041b Increase null verification threshold to 12 plies
Increasing depth limit to 10 plies seems stronger
after 16K games at 15"+0.05 (ELO: +3.56) and also
repeating the test at 60"+0.05 TC:

ELO: 2.08 +-3.1 (95%) LOS: 90.9%
Total: 16000 W: 2641 L: 2545 D: 10814

Moreover setting the limit to 12 is proved stronger
then limit set to 10 by direct SPRT test at 15"+0.05:

ELO: 2.56 +-2.0 (95%) LOS: 99.5%
Total: 46568 W: 9240 L: 8897 D: 28431

So we directly set the limit to 12, the strongest setup.

bench: 4361224
2013-04-09 23:29:58 +02:00
Joona Kiiski 7bad50773a Make use of asymmetric SEE 2013-04-05 19:05:32 +01:00
Joona Kiiski d3c3c4f8e7 Fix a silly bug 2013-04-05 19:05:32 +01:00
Joona Kiiski 2097cd1221 Introduce asymmetric SEE.
No functional change
2013-04-05 19:05:32 +01:00
Marco Costalba 0c1b40c5e2 Lower minimum allowed TT size to 1 MB
Setting a very low TT size could be used
for some specific testing.

No functional change.
2013-04-05 16:56:01 +02:00
Marco Costalba 8c10029df1 Revert "Double Impact of Gain tables"
This reverts commit 36c82b751c

Seems a regression against 2.3.1 tested with 20K games at 60"+0.05

With patch applied
ELO: 15.44 +-2.8 (95%) LOS: 100.0%
Total: 20000 W: 3928 L: 3040 D: 13032

Without patch applied
ELO: 18.76 +-2.8 (95%) LOS: 100.0%
Total: 20000 W: 3903 L: 2824 D: 13273

bench: 4781239
2013-04-05 08:59:38 +02:00
Hiraoka Takuya 6e2371a86b Don't early stop if we have a mated score
No functional change.
2013-04-04 21:39:48 +02:00
Joona Kiiski 36c82b751c Double Impact of Gain tables
Very unorthodox idea.

After 16000 games at 60"+0.05
ELO: 3.14 +-3.4 (95%) LOS: 96.6%
Total: 13407 W: 2278 L: 2157 D: 8972

bench: 4705335
2013-04-03 19:14:59 +02:00
Marco Costalba 7d42d02ec7 Set IID half way between d/2 and d-4
Master IID formula is depth / 2
Previous patch is depth - 4 * ONE_PLY

This one is the middle way:

(dept/2 + depth-4*ONE_PLY)/2  -> depth-2*ONE_PLY-depth/4

After 16000 games at 60+0.05 th 1
ELO: 4.08 +-3.1 (95%) LOS: 99.5%
Total: 16000 W: 2742 L: 2554 D: 10704

bench: 4781239
2013-03-30 22:26:30 +01:00
Marco Costalba c89274d8fb Merge branch 'master' into increase_iid 2013-03-29 22:50:04 +01:00
Marco Costalba f2638816bf Raise Min Split Depth
Raise the limit to 12 so to allow people to test
on many cores machines.

Suggested by Gary and Martin.

No functional change.
2013-03-25 20:04:49 +01:00
Marco Costalba 0b4ea54da9 Update bestValue when futility pruning (2)
Same idea of 5af8179647
in qsearch() but applied to search()

After 15500 games at 15+0.05
ELO: 4.48 +-3.4 (95%) LOS: 99.5%
Total: 15500 W: 3061 L: 2861 D: 9578

bench: 4985829
2013-03-24 23:32:21 +01:00
Joona Kiiski 09f1fdf52f Fix bogus mate scores in some positions
Always before pruning the move, it's important to check that:

bestValue > VALUE_MATED_IN_MAX_PLY

See example position:

8/2p1p3/P1NpP3/3k4/1P1BN3/2P1P3/2Q5/6K1 w - - 0 1
http://support.stockfishchess.org/discussions/problems/268-wrong-declaring-a-forced-mate-in-3-moves

This problem was present in 2.3.1, then it was fixed by my patch.

After 24000 games at 15+0.05
ELO: 2.40 +-4.4 (95%) LOS: 95.7%
Total: 24000 W: 4774 L: 4608 D: 14618

bench: 4465997
2013-03-23 21:28:51 +01:00
jundery d39b22927e Use ALL_PIECES value to reference attackedBy
No functional change
2013-03-23 12:17:12 +01:00
Marco Costalba 81e9cf043a Increase non-PV IID search depth
bench: 5146380
2013-03-22 20:57:31 +01:00
Marco Costalba 077e32efc9 Better document bitbase loop
Thanks to Lucas to spot the weak comment and
to Jundery to suggest a better one.

No functional change.
2013-03-19 19:08:41 +01:00
Gary Linscott 52f3e717fa Add KNPKB endgame
In a game vs Junior, SF had the option to trade into a winning
pawn endgame, and failed to do so. PGN at bottom.

This FEN was one key position: 8/2Nb1k2/6pp/4Pp2/5K1P/5PP1/8/8 w - - 5 62.

SF master chooses h5 here, with a fail high, which goes into the drawn KNPKB
ending. With the patch, SF correctly chooses Ke3, which maintains chances to win.

[Event "nTCEC - Stage 2a - Season 1"]
[Site "http://www.tcec-chess.net"]
[Date "2013.03.05"]
[Round "11.3"]
[White "Stockfish 210213"]
[Black "Junior 13.3"]
[Result "1/2-1/2"]
[Variant "normal"]

1. d4 f5 2. g3 Nf6 3. Bg2 e6 4. c4 d5 5. Nh3 c6 6. O-O Bd6 7. Bf4 Be7 8. Nd2 O-O
9. Qb3 a5 10. Rfd1 Ne4 11. Be3 Nd7 12. Nf4 Ndf6 13. f3 a4 14. Qc2 Nxd2 15. Bxd2
dxc4 16. Qxc4 b5 17. Qc3 Qb6 18. Rac1 e5 19. Nd3 exd4 20. Qxc6 Qxc6 21. Rxc6
Bd7 22. Rcc1 Be6 23. Bb4 Rae8 24. Bxe7 Rxe7 25. Nb4 Bc4 26. Bf1 Rd7 27. Rd2 Re8
28. Rcd1 Rc7 29. Ra1 Rd8 30. Rc1 Rdd7 31. Rcd1 Re7 32. Ra1 Nd5 33. Nc2 Ne3
34. Nxd4 Bxa2 35. Nxb5 Rc5 36. Nd4 Bf7 37. Kf2 g6 38. Rd3 Nxf1 39. Kxf1 Rc4
40. b3 axb3 41. Nxb3 Kf8 42. Rd8+ Re8 43. Rxe8+ Bxe8 44. Kf2 Ke7 45. Ra7+ Bd7
46. Ke1 Rc3 47. Rb7 Rc2 48. Kd1 Rc4 49. Kd2 Kd6 50. Kd3 Rc7 51. Rxc7 Kxc7
52. Kd4 Kd6 53. Nc5 Bb5 54. e4 Be2 55. e5+ Ke7 56. Ke3 Bd1 57. Kf4 h6 58. h3
Kf7 59. h4 Bc2 60. Na6 Ba4 61. Nc7 Bd7 62. h5 g5+ 63. Ke3 Ba4 64. f4 Bd1
65. fxg5 hxg5 66. h6 Kg6 67. e6 f4+ 68. gxf4 gxf4+ 69. Kxf4 Bh5 70. Ke5 Kh7
71. Kf6 Kxh6 72. Na6 Bg4 73. e7 Bh5 74. Nc7 Bg6 75. Nd5 Be8 76. Ne3 Kh7 77. Nc4
Kh6 78. Nd2 Kh5 79. Nf3 Kg4 80. Nd4 Bh5 81. Ne6 Be8 82. Nc5 Kf3 83. Kf5 Ke3
84. Ke5 Ke2 85. Kf4 Kd2 86. Ne4+ Kd3 87. Ke5 Ke3 88. Nf6 Bf7 89. Nd5+ Kf3
90. Kf5 Ke2 91. Ke4 Be8 92. Nc3+ Kd2 93. Kd4 Kc2 94. Nd5 Kd1 95. Nf6 Bf7
96. Ne4 Be8 97. Ke3 Kc2 98. Nd6 Bd7 99. Kd4 Kd1 100. Kd3 Ba4 101. Nc4 Bb5
102. Kc3 Be8 103. Nb2+ Ke1 104. Kd3 Kf2 105. Nd1+ 1/2-1/2

No functional change (just because bench does not change)
2013-03-18 20:40:06 +01:00
Marco Costalba 0586b51f9c Further increase SEE prune depth
After 16000 games at 60+0.05
ELO: 2.89 +-5.4 (95%) LOS: 96.5%
Total: 16000 W: 2775 L: 2642 D: 10583

bench: 5442365
2013-03-16 11:20:03 +01:00
Marco Costalba d2eeef89aa Revert "Check for easy move just once"
This reverts commit a24da071f0

Seems a regression when tested against 2.3.1

With this patch, have after 20000 games at 60+0.05, we have

ELO: 13.42 +-4.8 (95%) LOS: 100.0%
Total: 20000 W: 3746 L: 2974 D: 13280

Instead with the patch reverted:

ELO: 16.62 +-4.8 (95%) LOS: 100.0%
Total: 20000 W: 3816 L: 2860 D: 13324

Although we are within error bounds here we take the conservative
approach of not introducing changes that are not proved stronger
It doesn't mean that the change shall be weaker, simply that we
don't want to take any risk.

No functional change.
2013-03-16 11:12:35 +01:00
RyanTaker 70d20326b0 Improved Readability of Material
This is a non-functional change that simply changes the look
of the code to help clarity.

No functional change.
2013-03-15 09:14:00 +01:00
jundery ccf4ec6768 Do more work between prefetch and querying transposition table
More time to load the cache line before access

No functional change.
2013-03-12 19:58:32 +01:00
Gary Linscott a24da071f0 Check for easy move just once
Here the rational seems to be that if after one try easy
move detection fails then the easy move is not so easy :-)

After 15563 games at 60+0.05
ELO: 3.04 +-5.5 (95%) LOS: 97.0%
Total: 15563 W: 2664 L: 2528 D: 10371

No functional change.
2013-03-11 22:23:19 +01:00
Gary Linscott 3698d9aa55 Be more aggressive on trying to finish iterations
Increase MaxRatio to use more time when in trouble.

After 16000 games at 60+0.05
ELO: 4.89 +-5.4 (95%) LOS: 99.9%
Total: 16000 W: 2700 L: 2475 D: 10825

No functional change.
2013-03-11 19:07:55 +01:00
Marco Costalba 10429dd616 Increase see prune depth
This seems good at short TC controls.

After 10000 games at 20+0.05
ELO: 9.56 +-6.8 (95%) LOS: 100.0%
Total: 10000 W: 1949 L: 1674 D: 6377

Testing at long TC and regression testing is still
ongoing. So this is a bit speculative commit and
could be reverted in the future.

Also re-testing at long TC the SEE pruning in PV nodes
seems less effective (perhaps even a regression, but
still ongoing) so disabled for now.

bench: 4968764
2013-03-04 09:41:56 +01:00
Marco Costalba db322e6a63 Revert "Store moves sent with "position" UCI command"
This reverts commit 0d68b523a3.

After easy move semplification this machinery is not
needed anymore (because of we don't need to know if a
root move is a recapture)

No functional change.
2013-03-04 09:29:46 +01:00
Marco Costalba 45dba12c5b Simplify "easy move" detection
Detect a move as easy only if it is the only one ;-)
or if is much better than remaining ones after we
have spent 20% of search time.

Tests are ongoing, but it seems this semplification
stands. Anyhow it is experimental for now and could
be reverted/improved with further work Gary is
testing right now.

No functional change.
2013-03-04 09:27:00 +01:00
Marco Costalba ccad601389 Avoid locking/unlocking in a tight loop
After previous patch if split point master is
waiting for job and "Use Sleeping Threads" is
false (our condition for official releases) then
it will lock/unlock splitPoint mutex in a super
tight loop badly affecting performance.

Rewrite the code to lock only when we are about
to finish. Note that race condition on slavesMask
is anyhow fixed.

No functional change.
2013-03-04 09:07:48 +01:00
jundery d165d5af91 Fix race condition where idle_loop() gets called from Split()
SplitPoint member slavesMask wasn't read under lock

No functional change.
2013-03-04 08:52:24 +01:00
jhellis3 3ce43c20de Stop search if only 1 legal move
There is no point searching a move that is forced.
It wastes time while allowing computer opponents to
fill hash with 100% accuracy.

[edit: Condition moved together with "easy move" ones]

Bench identical: 4922272
2013-03-04 08:30:55 +01:00
Marco Costalba def50020ad Fix easy re-capture case
We detect an easy move as a recapture with an
high margin on the second best move.

Unfortunatly the recapture detection is broken
becuase we identify as a recapture any move that
follows an opponent's previous capture !

This patch fix the logic to correctly detect a
real re-capture.

No functional change.
2013-03-02 13:20:40 +01:00
Marco Costalba 0d68b523a3 Store moves sent with "position" UCI command
Store all the game moves until current position.

This will be used by next patch.

No functional change.
2013-03-02 13:08:50 +01:00
Marco Costalba 0e1ad3ad33 Rename sp to splitPoint
Still keep 'sp' name when used as local
variable with limited scope.

From Jundery.

No functional change.
2013-03-01 09:44:19 +01:00
jundery 0fc9a01933 Remove strange use of the ternary operator
Note that we read shared data without lock
protection, so code is theoretically prone to
torn reads. But, first splitPoint pointer
never changes, and alpha is of integer type so
it is read in a single DWORD access.

No functional change.
2013-03-01 08:05:47 +01:00
jundery 68d1bebd8e Split() clean up locking
Only unlock and relock when idle_loop() is actually called

No functional change
2013-03-01 07:57:18 +01:00
Marco Costalba 57b6df4874 Merge Lucas's "SEE pruning at PV nodes"
bench: 4922272
2013-02-27 08:14:00 +01:00
Marco Costalba bc38efd128 Remove pruning condition on alpha
Further simplifying on Lucas's idea, seems reliable
in tests:

ELO: 2.15 +-7 (95%) LOS: 84.9%
Total: 9999 W: 1831 L: 1769 D: 6399
2013-02-27 08:07:26 +01:00
Lucas Braesch 335b57b5ef Prune negative SEE moves also in PV nodes
This patch is actually the sum of two contributions that
have been tested independently:

1) Pruning of negative SEE moves in PV

After 10000 games at 20+0.05
ELO: 5.18 +-7 (95%) LOS: 99.2%
Total: 10000 W: 1952 L: 1803 D: 6245

2) Remove of bestValue > VALUE_MATED_IN_MAX_PLY condition

After 23000 games at 20+0.05
ELO: 1.63 +-4 (95%) LOS: 88.1%
Total: 23000 W: 4232 L: 4124 D: 14644

The whole patch as been re-tested at long TC with positive results:

After 10000 games at 60+0.05
ELO: 4.31 +-7 (95%) LOS: 98.3%
Total: 10000 W: 1765 L: 1641 D: 6594
2013-02-27 08:04:32 +01:00
Marco Costalba dbd28bc7f8 Avoid a tricky line in shelter_storm()
kf = (kf == FILE_A) ? kf++ : ....

is tricky becuase kf is updated twice and it happens
to do the right thing just by accident.

Rewrite in a better way.

Spotted by pdimov

No functional change.
2013-02-23 19:27:32 +01:00
Marco Costalba ccf21f5595 Convert Readme to markdown format
Looks better on GitHub, that supports this format.

No functional change.
2013-02-23 17:05:28 +01:00
Marco Costalba 1d1fcf80a0 Use DD-MM-YY as date format
In engine name and version number.

No functional change.
2013-02-21 07:19:48 +01:00
Marco Costalba 373503f4a9 Statically link std libraries under mingw
Allows for easier redistribution.

No functional change.
2013-02-21 05:51:04 +01:00
Marco Costalba 9a1d5f0f1d Merge Gary's bishop_pin patch
Give a bonus if a bishop can pin a piece or can
give a discovered check through an x-ray attack.

Seems good after 24000 games at 15"+0.05 (single thread):

ELO: 12.30 +- 99%: 5.79 95%: 4.40 LOS: 100.00%
Total: 24000 W: 4931 L: 4082 D: 14987

bench: 4917064
2013-02-20 12:39:09 +01:00
jundery d5e49a3ad4 Print leading zeroes in hash keys
And convert to uppercase. Reset the stream to dec too.

[Edit: Also fixed the hash key in Position::pretty()]
2013-02-19 20:06:01 +01:00
Gary Linscott ea4e22be1d Merge branch 'master' into bishop_pin_clop 2013-02-19 10:31:52 -05:00
Gary Linscott dc8c6ea2d1 Bring back original bonus 2013-02-19 10:31:50 -05:00
Marco Costalba c5ec94d0f1 Update copyright year
No functional change.
2013-02-19 07:54:14 +01:00
Gary Linscott d570260a28 Back to CLOP average values 2013-02-16 22:36:58 -05:00
Marco Costalba 76caef8ba1 Account for gamePly after each move
Rename startPosPly to gamePly and increment/decrement
the variable after each do/undo move. This adds a little
overhead in do_move() but we will need to have the
game ply during the search for the next patches now
under test.

Currently we don't increment gamePly in do_null_move()
becuase it is not needed at the moment. Could change
in the future.

As a nice side effect we can now remove an hack in
startpos_ply_counter().

No functional change.
2013-02-16 12:44:17 +01:00
Marco Costalba 5a156df719 Merge Gary's king safety tweak
Still well within error bars, so probably not a big improvement,
but may be worthwhile. I will let the test keep running. The idea
for the tweak came from the TCEC game against Houdini. Stockfish saw
it as a draw, well past when it should have seen problems. King safety
was off, since it was QN and pawns only, but in fact the king was
quite vulnerable.

After 8000 games at 60/1 (Gary's test)

ELO: -0.43 +- 99%: 10.02 95%: 7.62
Wins: 1235 Losses: 1245 Draws: 5520 Total: 8000

PGN of game against houdini. Moves 55-59 it was seeing a draw, and
Houdini was seeing a good sized advantage for black. With the change,
Stockfish now recognizes that moving the king there is a bad idea.

[Event "nTCEC - Stage 1 - Season 1"]
[Site "http://www.tcec-chess.net"]
[Date "2013.02.07"]
[Round "4.2"]
[White "Stockfish 2.31"]
[Black "Houdini 3.0"]
[Result "0-1"]
[Variant "normal"]

1. Nf3 Nf6 2. c4 e6 3. Nc3 Bb4 4. Qc2 O-O 5. a3 Bxc3 6. Qxc3 b6 7. b4 a5 8. Bb2
axb4 9. axb4 Rxa1+ 10. Bxa1 Na6 11. e3 Qe7 12. b5 Nc5 13. Qc2 Bb7 14. Be2 d6
15. O-O Ra8 16. Bb2 h6 17. Ra1 Rxa1+ 18. Bxa1 e5 19. Qa2 Be4 20. Ne1 Bg6
21. Bb2 Kh7 22. f3 Nfd7 23. Bc3 h5 24. Qa1 h4 25. Kf1 Qf6 26. Qa7 Qd8 27. Qa2
e4 28. Qa1 h3 29. g3 exf3 30. Nxf3 Bf5 31. Bd4 g6 32. Kf2 Kg8 33. Qa3 Bg4
34. d3 Qc8 35. Bxc5 bxc5 36. Ke1 Qb7 37. e4 Bxf3 38. Bxf3 Ne5 39. Be2 Qc8
40. Qa6 Qd8 41. Qa5 Kh7 42. Kd1 Kg7 43. Qa7 Kg8 44. Qa5 Kh7 45. Qa7 Kg7 46. Qa5
Qb8 47. Kd2 Kh7 48. Kc2 Kg8 49. Qa6 Qd8 50. Qa5 Qf6 51. Qe1 Kg7 52. Qf1 Qe6
53. Qe1 Qd7 54. Qc1 Qe8 55. Kc3 Qa8 56. Kb3 Kh7 57. Qa3 Qd8 58. Qc1 c6 59. Qc3
Qb6 60. Ka4 Qb7 61. Qd2 Nd7 62. Qc3 Qa7+ 63. Kb3 Kg8 64. Bg4 cxb5 65. cxb5 Nb6
66. Bxh3 Qa4+ 67. Kb2 Qd1 68. Ka3 Qe2 69. Kb3 Qh5 70. Bg2 Qxh2 71. Qf6 Qxg3
72. Bf1 Qe3 73. Kc2 Na4 74. b6 Nxb6 75. Qd8+ Kg7 76. Qxb6 Qf2+ 77. Kc3 Qxf1
78. Qxd6 Qf6+ 79. Qxf6+ Kxf6 80. Kc4 g5 81. Kxc5 Ke5 82. d4+ Kxe4 83. d5 g4
84. d6 g3 85. d7 g2 86. d8=Q g1=Q+ 87. Kb5 Qb1+ 88. Ka4 Qa2+ 89. Kb4 f5
90. Qe8+ Kf4 91. Qg6 Qd5 92. Qg1 Qe4+ 93. Ka5 Qe2 94. Qg8 Kf3 95. Qd5+ Qe4
96. Qd1+ Kg2 97. Qd2+ Kf1 98. Qh2 f4 99. Qh3+ Ke2 100. Qg4+ Kd2 101. Qg5 Kc2
102. Qh4 0-1

bench: 5518286
2013-02-15 16:25:33 +01:00
Marco Costalba e0dfb0bc34 Further speed up bitbase generation
Another trick, along the same lines of previous
patch. This time we first check positions with
white side to move that, becuase we start with
pawn on rank 7, are easily classified as wins,
then black ones.

Number of cycles reduced to 15 !

Becuase now it is faster we can remove a lot of
code to detect theoretical draws. We will calculate
them anyhow, although a bit slower, but the speed
up trick more than compensates it.

Verified that generated bitbases match original ones.

No functional change.
2013-02-15 11:58:33 +01:00
Gary Linscott 3922ac2fe4 Add new clop tuned value 2013-02-14 20:29:24 -05:00
Gary Linscott 66a1a77487 Merge branch 'master' into bishop_pin_clop 2013-02-13 21:41:15 -05:00
Gary Linscott cd0ecace19 Revert "Use CLOP mean value instead of max"
This reverts commit d0c2faa5fd.
2013-02-13 21:40:38 -05:00
Marco Costalba 10d29add18 Speedup KPK bitbase of 25%
Change the way the index is coded so that
now looping from 0 to IndexMax generates
the pawns from RANK_7 down to RANK2.

Becuase positions with pawns at RANK_7
are easily classified as wins/draws, this
small trick allows to reduce the number
of needed iterations from 30 down to 26!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-02-13 20:13:56 +01:00
Marco Costalba f2950ae206 Simplify bitbase.cpp
Use a std::vector to store positions and
rearrange KPKPosition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-02-13 20:13:44 +01:00
Gary Linscott d0c2faa5fd Use CLOP mean value instead of max 2013-02-13 12:21:16 -05:00
Gary Linscott 671f24ff35 Merge branch 'simplify_eval' into bishop_pin_clop 2013-02-13 12:19:54 -05:00
Gary Linscott f32f899467 CLOP tuned 2013-02-13 00:12:02 -05:00
Gary Linscott 17b71fe51d Add clop parameters 2013-02-12 00:10:21 -05:00
Gary Linscott 30c2e3828a Merge branch 'master' into simplify_eval 2013-02-11 10:26:25 -05:00
Gary Linscott a7cdf7299a Bishop pins only 2013-02-11 10:26:18 -05:00
Marco Costalba 733d0099b2 Rename and de-templetize sort()
Rename to insertion_sort so to avoid confusion
with std::sort, also move it to movepicker.cpp
and use the bit slower std::stable_sort in
search.cpp where it is used in not performance
critical paths.

No functional change.
2013-02-11 00:09:21 +01:00
Marco Costalba 0be7b8c542 Further simplify first_entry()
We can encode the ClusterSize directly in the
hashMask, this allows to skip the left shift.

There is no real change, but bench number is now
different because instead of using the lowest order
bits of the key to index the start of the cluster,
now we don't use the last two lsb bits that are
always set to zero (cluster size is 4). So for
instance, if 10 bits are used to index the cluster,
instead of bits [9..0] now we use bits [11..2].
This changes the positions that end up in the same
cluster affecting TT hits and so bench is different.

Also some renaming while there.

bench: 5383795
2013-02-09 16:37:20 +01:00
Marco Costalba c698362680 Microptimize first_entry() for 32bits
Do a 32bit bitwise 'and' instead of a 64bit
subtract and bitwise 'and'.

This is possible because even in the biggest
hash table case (8GB) the number of entries
is 2^29 so storable in an unsigned int.

No functional change.
2013-02-09 10:55:38 +01:00
Marco Costalba fe3352665b Retire TTCluster and simplify TT
Also some renaming while there.

No functional change.
2013-02-09 10:55:20 +01:00
Marco Costalba 3f44be9baa Simplify move_to_san()
Nicely simplify disambiguation code.

No functional change.
2013-02-09 07:43:53 +01:00
Gary Linscott 0c06b4509d Slight tweak to king safety. Bench: 5534531 2013-02-08 08:53:13 -05:00
Marco Costalba e5bc79fb9c Retire slavesPositions
Save the current active position in each Thread
instead of keeping a centralized array in struct
SplitPoint.

This allow to skip a memset() call at each split.

No functional change.
2013-02-08 11:45:33 +01:00
Marco Costalba 880726c13a Add const qualifer to go()
Obsolete renmant of when position was directly
passed to the search instead of being copied
for the main thread as is now.

From Jundery.

No functional change.
2013-02-08 10:07:09 +01:00
Marco Costalba 50a7200b18 Workaround value-initialization in MSVC
The syntax splitPoints() should force the compiler to
value-initialize the array and because there is no
user defined c'tor it falls back on zero-initialization.

Unfortunatly this is broken in MSVC compilers, because
value initialization for non-POD types is not supported,
so left splitPoints un-initialized and add in split()
initialization of slavesPositions, that is the only
member not already set at split time.

This fixes an assert under MSVC when running with
more than one thread.

Spotted and reported by Jundery.

No functional change.
2013-02-08 09:20:40 +01:00
Gary Linscott c67fb8ef04 Add KBPKP endgame
It is a draw if pawns are on G or B files, weaker pawn is
on rank 7 and bishop can't attack the pawn.

No functional change (because it is very rare and does not appear in bench)
2013-02-07 06:51:55 +01:00
Marco Costalba 14c2c1395b Change slave_available() API
To return a pointer to the available
thread instead of a bool. This allows
to simplify the core loop in split().

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-02-06 20:48:26 +01:00
jundery 88c3670edf Rename posKey stored in the transposition table
[Edit: Slightly extended by me]

No functional change.
2013-02-06 08:03:37 +01:00
jundery f69c185e02 Add const qualifer to check_is_dangerous
No functional change.
2013-02-06 07:49:40 +01:00
Marco Costalba bf706c4a4f Slightly change split() API
This function "returns" two values: bestValue and bestMove

Instead of returning one and passing as pointer the other
be consistent and pass as pointers both.

No functional change.
2013-02-05 06:35:38 +01:00
Marco Costalba 1a414cd9cb Derive ThreadPool from std::vector
Prefer sub-classing to composition in this case.

No functional change.
2013-02-04 22:59:20 +01:00
Marco Costalba 91427c8242 Move split() under Thread
Previous renaming patch suggested this reformat:
when a better naming leads to a better code!

No functional change.
2013-02-04 22:17:04 +01:00
Marco Costalba b8c5ea869c Some renaming in split()
Naming suggested by jundery.

No functional change.
2013-02-04 22:00:41 +01:00
Marco Costalba 6560e4cc5b Be clear about not LMR the ttMove
Currently a ttMove is reduced with ss->reduction = DEPTH_ZERO,
so it is actually not reduced (as it should be), but the
trick works just becuase it happens that ttMove is the first
to be tried and

reduction(depth, 1)

Always returns zero. So explicitly forbid reduction of ttMove
in the LMR condition. This is much clear and self-documented.

No functional change.
2013-02-03 11:14:21 +01:00
Marco Costalba 08d615cc95 Templetize score_xxx() functions
So to be style-wise aligned with the corresponding
generate() functions.

No functional change.
2013-02-03 10:11:12 +01:00
Marco Costalba 1c4e6d7ea2 Rename prevents_move() to refutes()
Better! From DiscoCheck.

No functional change.
2013-02-03 09:23:04 +01:00
Marco Costalba 5f58db8c99 Correctly score enpassant captures
Surprisingly this rare case was not considered
when scoring a capture.

Also take in account that in the promotion case
we gain a new piece (typically a queen) but we
lose the promoting pawn.

These small issues were present since Glaurung times!

Found while browsing DiscoCheck sources

bench: 5400063
2013-02-03 09:08:32 +01:00
Marco Costalba ddbe6082c4 Unify History and Gains under a single Stats class
Handling of History and Gains is almost the same, with
the exception of the update logic, so unify both
classes under a single Stats struct.

No functional change.
2013-02-02 17:45:09 +01:00
Marco Costalba 53051eefc7 Retire history.h
And move the contents to movepick.cpp, where they are
mostly used.

Idea from DiscoCheck.

No functional change (bench 5379503)
2013-02-02 16:54:35 +01:00
Gary Linscott a72710c660 Simplify eval take 2. Bench 5097444 2013-02-02 10:19:59 -05:00
Marco Costalba 9f94d22801 Restore "fail-low of reduced" and close regression
This reverts "Threat Extensions" and is the last of
this revert series.

In single-thread tests we should now be on par with 2.3.1
2013-02-02 07:16:33 +01:00
Marco Costalba 0901e12102 Revert "Simplify Evaluation"
This reverts commit 496c7497cb
2013-02-02 06:44:04 +01:00
Marco Costalba 58c9fbacc7 Revert "Extend full 3 fold detection to PvNodes" 2013-02-02 06:41:05 +01:00
Marco Costalba 483c98a69e Rewrite do_castle_move()
And handle the castle directly in do/undo_move().
This allow to greatly simplify the code.

Here the beast is the nasty Chess960 that is
really tricky to get it right because could be
that 'from' and 'to' squares are the same or
that king's 'to' square is rook's 'from' square.

Anyhow should work: verified on all Chess960
starting positions.

No functional and no speed change also in Chess960.
2013-01-28 13:40:47 +01:00
Marco Costalba 2218a5836a Rewrite do_null_move()
Use a more traditional approach, along the same lines
of do_move().

It is true that we copy more in do_null_move(), but we
save the work in undo_null_move(). Speed test shows the
new code to be even a bit faster.

No functional change.
2013-01-27 12:15:02 +01:00
Marco Costalba 76a0d3c05a Get rid of some locals in do_castle_move()
Rewrite the logic to get rid of kBefore and rBefore.

No functional change.
2013-01-27 11:03:55 +01:00
Marco Costalba 52cab06fff Don't prefetch if not needed
Prefetch access to hash tables only in case we
have changed pawn or material hash keys.

No functional change.
2013-01-27 10:19:11 +01:00
Marco Costalba bd87ab9ff5 Retire generate_king_moves()
We have only one call place so inline its content.
BTW, function is already declared as FORCE_INLINE.

Also some small refactoring while there.

No functional change.
2013-01-26 22:43:58 +01:00
Gary Linscott 57797822f8 Bring back just bishop pins 2013-01-26 15:35:00 -05:00
Marco Costalba 7062db7cb2 Clarify slavesMask usage
When a thread is allocated a bit is set in slavesMask.
This bit corresponds to the thread's index field that,
because it happens to be the position in the threads
array, eventually it is equal to the loop index 'i'.

But instead of relying on this 'coincidence', explicitly
use the 'idx' field so to clarify slavesMask usage.

Backported from c++11 branch.

No functional change.
2013-01-26 14:38:51 +01:00
Marco Costalba 166cc0292c Revert "Further push singular extension"
This reverts commit 4c91dbc28e

Seems a regression on extended test by both Gary and me.
2013-01-26 10:20:46 +01:00
Marco Costalba 496c7497cb Merge branch 'simplify_eval' of https://github.com/glinscott/Stockfish
Test results are looking good after 12500 games.

ELO: 6.55 +- 99%: 8.02 95%: 6.09
LOS: 99.99%
Wins: 1968 Losses: 1732 Draws: 8813

Also, here are the noise.py results, which seem to have stabilized:
Games: 12526 , result: [1969, 1734, 8823]
Estimated ELO: 6.94963842777
Noise as function of number of games:
['81.89', '565.26', '110.87', '104.39', '38.22', '49.98', '18.56', '16.76',
'11.02', '8.90', '17.36', '9.84', '10.81', '5.13', '6.22', '3.32', '5.83',
'7.21', '15.27', '1.63', '4.04', '9.51', '0.54', '0.75', '1.06', '2.93',
'4.59', '6.85', '13.62', '9.87', '14.74', '20.46', '22.18', '24.33', '31.02',
'34.99', '35.22', '33.22', '32.46', '37.02', '29.10', '36.34', '42.11', '39.33',
'26.16', '28.25', '35.42', '31.04', '29.26', '23.91', '22.52', '23.49', '20.00',
'24.39', '17.22', '16.50', '10.69', '9.15', '9.57', '4.77', '6.67', '3.87', '2.57',
'2.84', '2.60', '3.32', '2.08', '2.93', '4.47', '4.41', '4.83', '4.86', '6.40',
'5.98', '6.10', '6.83', '5.83', '6.22', '5.71', '8.52', '9.25', '5.98', '7.52',
'7.76', '8.76', '8.55', '8.64', '7.19', '5.83', '4.59', '4.77', '4.26', '4.98',
'5.29', '5.41', '4.92', '5.59']

bench: 5229106
2013-01-26 10:18:36 +01:00
Gary Linscott e83b9075ff Simplify evaluation 2013-01-24 08:54:13 -05:00
Marco Costalba 6950d07bf4 Small reformat of split()
No functional chhange.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-01-21 23:31:33 +01:00
Marco Costalba 054d117d25 Fix an idiotic icc warning
Intel Compiler has 'invented' this pearl:

warning #1476: field uses tail padding of a base class

Just becuase we have subclassed MainThread and added
the field 'bool thinking'.

Pure nosense. Silence the warning.

No functional change.
2013-01-20 17:36:24 +01:00
Marco Costalba 62b32a4737 Futher renaming in thread.cpp
No functional change.
2013-01-20 17:35:55 +01:00
Marco Costalba 588670e8d2 Big renaming in thread stuff
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-01-16 20:00:05 +01:00
Marco Costalba c465f4c4df Fix race while exiting
Fix again TimerThread::idle_loop() to prevent a
theoretical race with 'exit' flag in ~Thread().

Indeed in Thread d'tor we raise 'exit' and then
call notify() that is lock protected, so we
have to check again for 'exit' before going to
sleep in idle_loop().

Also same change in Thread::idle_loop() where we
now check for 'exit' before to go to sleep.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-01-16 19:58:55 +01:00
Lucas Braesch 8737b26a23 Remove Threat Extension
Great code simplification: - instead do not futility
prune threat refutations. allows_move() is therefore removed.

4000 games at 50,000 nodes/move:
1085-989-1926 [51.2%] LOS=98.3%

4000 games in 10"+0.1"
756-751-2493 [50.1%] LOS=55.1%

EDIT: I have retested the patch of Lucas in a slightly different form
(without pruning in PvNode) and test mre or less confirms that
60 lines of code are totally unuseful:

After 6195 games at 15"+0.05"
1333 - 1325 - 3537 ELO 0

bench 5140990
2013-01-15 17:03:56 +01:00
Marco Costalba 78a9531773 Fix a bug in timer loop
Silly logic bug introduced in dda7de17e7

Timer thread, when msec = 0, instead of going
to sleep, calls check_time() in an endless loop.

Spotted and reported by snino64 due to abnormally
high CPU usage.

No functional change.
2013-01-14 19:32:30 +01:00
Marco Costalba d1143794a0 Polymorphic Thread hierarchy
Subclass MainThread and TimerThread and declare
idle_loop() virtual. This allow us to cleanly
remove a good bunch of hacks, relying on C++
polymorphism to do the job.

No functional change.
2013-01-14 02:01:37 +01:00
Marco Costalba e70eae2c91 Don't use do_sleep flag
Rename it is_finished and use it only in main
thread to signal search is finished. This allows
us to simplify the complex SMP logic.

Ultra tricky patch: deep test is required under
wide conditions like pondering on and option
"Use Sleeping Threads" set to false.

No functional change.
2013-01-14 00:02:32 +01:00
Marco Costalba 99ae47716a Re-add the hack
This reverts commit 869c924410

I misunderstood here. Actually it can happen that
thread is created but still not entered idle_loop
and at the same time start_searching() is called.

Becuase 'do_sleep' is set start_searching() will
set it to false and start the search, but when,
at last, the thread enters idle_loop(), resets
the flag and goes to sleep: not what we want.

Revert the hack waiting for a better solution
in the next patches.

No functional change.
2013-01-13 23:56:04 +01:00
Marco Costalba dda7de17e7 Retire set_timer()
Also assure in Thread::timer_loop() that when
timer interval is 0 (timer is disabled) we
never call check_time()

No functional change.
2013-01-13 18:24:43 +01:00
Marco Costalba 869c924410 Retire obsolete race hack
This hack was introduced in d282cf6964
to workaround a race with start_searching(),
but these days is no more needed.

No functional change.
2013-01-13 17:05:30 +01:00
Marco Costalba ea6c1f7a17 Retire Threads wake_up() and sleep()
These functions are used in just one place.
And generalize wait_for_stop()

No functional change.
2013-01-13 16:57:40 +01:00
Marco Costalba 81cd7d787e Rename wake_up() to notify_one()
To align to C++ std::thread conventions.

No functional change.
2013-01-13 16:43:26 +01:00
Marco Costalba a523cea772 Unify 'ponderhit' handling
Finally we can now merge the 'ponderhit' case with
'stop' and 'quit'.

The patches have been done step by step to help debugging
becuase this is really tricky code.

No functional change.
2013-01-13 14:39:41 +01:00
Marco Costalba a66a7c3870 Small change to "ponderhit" handling
Reset Limits.ponder only if search continue, but if
we are going to stop the search there is no need
(and is also confusing) to clear the 'ponder' flag.

This mimics the behaviour upon rceiving 'stop' when
pondering.

No functional change.
2013-01-13 14:32:30 +01:00
Marco Costalba 89a89eb605 Simplify and rename wait_for_stop_or_ponderhit()
Setting stopOnPonderhit is now done by the caller.

No functional change.
2013-01-13 14:15:19 +01:00
Marco Costalba 3b14b17664 Simplify a condition in search()
And rearrange best value update in case of SpNode.

No functional change.
2013-01-13 13:17:16 +01:00
Marco Costalba 3a836dab59 Clarify SAN disambiguation in case of a pinned piece
In SAN notation when two pieces of the same type can
move to a given destination square, a disambiguation
additional info (like starting file) shall be added
to the SAN move.

If one of the two pieces is pinned, the corresponding
move _could_ be illegal and in this case disambiguation
is not needed. But to be pinned alone it is not enough
to deduce that the move is illegal, for instance in this
position:

R3rk2/2r6/8/8/8/8/8/K7 b - - 0 1

The move Rc8 is ambiguous although the rook in e8 is pinned
and the correct SAN notation should be Rcc8.

No functional change.
2013-01-12 13:21:14 +01:00
Marco Costalba e1191b35e8 Async 'stop' command
Don't wait for the search to finish after a 'stop'
command, but keep processing the GUI input if any.

Also explicitly wake up the main thread (that could be
sleeping) after a 'stop' or 'quit' command and do not
rely on wait_for_search_finished() doing it for us.

This patch cleans up the code and functions's definitions,
but it is risky and needs a good test under different
conditions to be sure it does not introduces hungs up.

No functional change.
2013-01-12 12:06:55 +01:00
Marco Costalba edce2a8448 Revert so called "fromNull patch"
Revert patch c581b7ea36

Seems a regression after testing from Gary:
ELO: 7.24 +- 99%: 17.03 95%: 12.93
LOS: 97.86%
Wins: 439 Losses: 381 Draws: 1962

And mine:
After 5410 games at 15"+0.05
Wins: 936 Losses: 1141 Draws: 3333  ELO -13

Moreover we know that there is a regression in the range
of patches which include the fromNull patch.

Probably this is not the only regression since 2.3.1 and
perhaps the idea under fromNull is good, but at the moment,
while in deep regression hunting, better to be on the safe
side and revert it entirely.

My guess on why this is a regression is that using the
negated evaluation of previous ply in case of null search
fails to take in account the king safety asymmetry between
the two colors. This is of course just a guess.

bench 5503830
2013-01-06 23:06:20 +01:00
Marco Costalba 9b1cf3cf43 Have fun with union in book.cpp
Fancy way to use an union to map polyglot
zobrist keys in one go.

Also some renaming while there.

No functional change.
2013-01-06 12:06:19 +01:00
Marco Costalba bff65a211f Retire 'Cowardice' and 'Aggressiveness' UCI options
They are not self-describing and create a lot of user
requests about them.

Given that the values are already well tuned there
is no need to expose them as UCI options.

No functional change.
2013-01-04 17:11:24 +01:00
Marco Costalba 2d60995c00 Retire 'mate in x' hack
Sometimes is faster, but not always and on very long mates
produces strange scores probably due to truncation of PV
artifacts.

So simply perform normal search also in case of UCI 'mate x'
command, with the only difference that when a mate in x is
found search returns immediately.

No functional change.
2013-01-04 16:30:46 +01:00
Marco Costalba 0454bbc54f Don't exit if unable to find bench file
Now that we can call 'bench' command also from
interactive terminal it makes no more sense to
exit the application if the user types a wrong
file name.

No functional change.
2013-01-04 14:52:21 +01:00
Marco Costalba 900e2d4e1e Teach file_to_char() about upper/lower case
This allows to further simplify Position::fen()

No functional change.
2013-01-04 14:45:04 +01:00
Marco Costalba 9d1151575d Reformat FEN construction
Simplify and shrink code.

No functional change.
2013-01-04 13:38:14 +01:00
Marco Costalba 193741218c Remove some obsolete asserts on TT values
Now that insert_pv_in_tt() stores VALUE_NONE in
TT's position evaluation those 2 asserts are
obselete.

No functional change.
2012-12-31 17:20:39 +01:00
Marco Costalba 896420b166 Allow to pass a 'seed' to RKISS
This somewhat simplifies the code.

Suggested by Lucas Braesch.

No functional change.
2012-12-31 11:59:53 +01:00
Marco Costalba 009a0f88e0 Micro-optimization in evaluate_space()
Since &-ing with SpaceMask restricts the set to the home
half of the board, it is possible to use just one popcount
instead of 2 by shifting "safe" to the other half of the
board. This gives a small speedup especially on systems
where hardware popcount is not available.

Patch kindly sent by Richard Vida.

No functional change.
2012-12-31 11:34:18 +01:00
Marco Costalba e1d681458e Add 'mate' limit to 'bench' command
It is now possible to run SF on a 'mate in x' testsuite.

For instance in case of a file with fen strings of
positions with mate in 10 we can now 'bench' on it:

stockfish bench 128 1 10 mate_in_10.epd mate

No functional change.
2012-12-30 15:49:02 +01:00
Marco Costalba ce063f59cd Handle UCI command "mate in x moves"
Following a user request I added the handling of UCI:

go mate x

Currently we just return from a PV node if x moves have been
done. Probably not the best approach. I have looked at Fruit/Toga
sources and there is even simpler: engine falls back on a fixed
depth search.

No functional change.
2012-12-30 14:43:23 +01:00
Marco Costalba 3cf6471738 Revert evaluation cache
And return on using TT as backing store for position
evaluations.

Tests (even on single thread) show eval cache was a regression.
In multi thread result should be even worst because eval cache
is a per-thread struct, while TT is shared.

After 4957 games at 15"+0.05 (single thread)
eval cache vs master 969 - 1093 - 2895  -9 ELO

So previous reported result of +18 ELO was probably due to an
issue in the testing framework (a bug in cutechess-cli) that
has been fixed in the meanwhile.

bench: 5386711
2012-12-27 13:57:17 +01:00
Marco Costalba f78b68b7ff Add list of legal moves to Position::pretty()
Along the same lines of previous patch now we add
the list of the legal moves in the given position.

No functional change.
2012-12-27 11:34:48 +01:00
Marco Costalba e9ab7353de Add checkers info to Position::pretty()
In case current position is under check, list the
squares of the checker(s) pieces.

This should satisfy a specific user request.

No functional change.
2012-12-26 18:28:45 +01:00
Marco Costalba 9d1978e217 Remove two obsolete asserts in prevents_move
Now that this function is called also to calculate
move's extensions the asserts are no more valid.

No functional change.
2012-12-26 12:27:41 +01:00
Marco Costalba db097921bc Rename yields_to_threat and prevents_threat
Follow Lucas suggestions and better name these
two functions.

No functional change.
2012-12-26 12:21:59 +01:00
Marco Costalba 894c43a1d6 Introduce Null Threat extension
In case of null search at low depths returns a fail low
due to a threat then, rather than return beta-1 (to cause
a re-search at full depth in the parent node), we set a flag
threatExtension = true (false by default) that will cause
moves that prevent the threat to be extended of one ply
in the following search.

Idea and patch is by Lucas Braesch.

Lucas also did the tests:
1500 games in 5"+0.05":
SF_threatExtension vs SF_20121222: 366 - 331 - 803 [51.2%] LOS=90.8%

3000 games in 10"+0.1":
SF_threatExtension vs SF_20121222: 610 - 559 - 1831 [50.8%] LOS=93.2%

Tests confirmed by Gary after 10570 games,
ELO: 2.79 +- 99%: 8.72 95%: 6.63
LOS: 94.08%
Wins: 1523 Losses: 1438 Draws: 7607

And finally by me at 15"+0.05, single thread, 3824 games
threatExtension vs master 768 - 692 - 2364  +7 ELO

bench 4918443
2012-12-25 19:17:27 +01:00
Marco Costalba b5b799b5ab Fix a couple of extra spaces
No functional change.
2012-12-25 18:48:41 +01:00
Marco Costalba e82382703c Retire Position::in_check()
It is redundant with Position::checkers()

No functional change.
2012-12-25 17:59:35 +01:00
Marco Costalba 3b49aeb4f2 Retire Position::move_is_legal()
Use the new contains() method of struct MoveList

No functional change.
2012-12-25 11:51:08 +01:00
Marco Costalba 423c6d8a8a Small tweak in is_pseudo_legal()
This is difficult code becuase a bug here could lead
to very subtle crashes in case of SMP games where we
have TT move corruption due to concurrent access.

Anyhow I have fully verified te code throwing at it
random moves. It shoudl work.

No functional change.
2012-12-25 11:31:32 +01:00
Marco Costalba 158014b39d Introduce namespace Pawns
And retire old struct PawnTable along the same lines
of previous patch.

No functional change.
2012-12-22 11:38:36 +01:00
Marco Costalba 231f62baf7 Introduce namespace Material
And retire old struct MaterialTable simplifying the code.

No functional change.
2012-12-16 12:58:39 +01:00
Marco Costalba 52bbf372bb Don't need to check for bestValue < beta to split
With rearrangement of fail high code this condition
is no more necessary.

Found by Jörg Oster.

No fuctional change.
2012-12-15 13:11:10 +01:00
Marco Costalba 3ddf91d9d1 Remove an extra semicolon
No functional change.
2012-12-15 11:20:04 +01:00
Marco Costalba a2f46446cf Revert store of distinct upper and lower bounds
Test by Joona prooves the new feature don't value 70 added lines of code.

Grand totals after 10040 games (crashes: 0) for tt_both

master_9edc7 - 6a93488_6a934: 1756 - 1688 - 6596 ELO +2 (+- 2.7)

Confirmed by test of Gary:

After 8680 games:
ELO: 0.80 +- 99%: 9.62 95%: 7.31
LOS: 65.38%
Wins: 1288 Losses: 1268 Draws: 6130

Thanks a lot to both for testing it !!!

bench 5149248
2012-12-15 11:18:52 +01:00
Marco Costalba 9edc7d6958 Merge branch 'eval_cache'
Unusually good result. Defenitly needs further verifications.

After 2160 games at 15"+0.05
Mod vs Orig 486 - 367 - 1307 ELO +19

bench: 6261882
2012-12-10 09:26:02 +01:00
Marco Costalba 55db871472 Fix comparison with alpha, not beta
This silly bug seems the reason of the unsual bench
value.

bench: 6261882
2012-12-09 14:19:21 +01:00
Marco Costalba da98a45bcb Ensure valueLower <= valueUpper
In case a TTEntry stores both an upper and a lower bound
ensure that upper bound is not smaller than lower bound.

bench 1813815
2012-12-09 14:14:44 +01:00
Marco Costalba feeafb0a50 Store distinct upper and lower bound scores
This is more complex than what I'd like but I
was unable to split in small chunks.

Here we add 2 slots to TTEntry (valueUpper and depthUpper)
so that sizeof(TTEntry) returns to the original 16 bytes
and we can pack exactly 4 entries in a 64 bytes cache line.

Now we save an upper bound score alongside a lower (exact)
score. The idea is to increase TT cut-offs rates becuase
there is now an higher probability for a node to use TT info.

This patch is highly experimental and probably needs further
steps as is hinted by an unrealistic bench number:

bench: 2022385
2012-12-09 13:15:50 +01:00
Marco Costalba 22c557ca7c Micro-optimize color_of()
In almost all cases we already know in advance that
color_of() argument is different from NO_PIECE.

So avoid the check for NO_PIECE in color_of() and
test at caller site in the very few places where
this case could occur.

As a nice side effect, this patch fixes a (bogus)
warning under some versions of gcc.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-12-08 12:32:48 +01:00
thaspel 6a93488291 Update Readme.txt now that we support 64 threads
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-12-04 19:57:16 +01:00
Marco Costalba 6fa83f5188 Merge branch 'eval_cache'
Use an eval cache instead of TT to store node
position evaluations.

It is already an improvment and, because it frees
two TT entry slots, paves the way to extend TT to
store both upper and lower bounds.

After 4855 games, single thread, 15"+0.05
Mod vs Orig 1165 -920 - 2770 ELO +18

bench: 5149248
2012-12-04 08:05:15 +01:00
Marco Costalba ce248e7920 Increase MAX_THREADS to 64
And document why this is an hard limit. It
seems for some (lucky) people 32 threads
are not enough.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-12-04 07:57:15 +01:00
Marco Costalba 23bdd06442 Reintroduce eval optimizaion from null search
Now that conversion to eval cache is finished
we can reintroduce this optimization.

bench: 5149248
2012-12-01 18:11:01 +01:00
Marco Costalba 98cd8239cc Don't save eval score in TT
This patch completes the removal of eval info
in TT table.

No functional change.
2012-12-01 15:19:50 +01:00
Marco Costalba 2a585b63b8 Don't use TT just to save a node evaluation
In search(), after we evalute the position, in case there
isn't any TT entry we create one with just the evaluation
score.

This patches removes that code. The reason becuase the patch
deserves a single commit it is becuase introduces a (very small)
functional change due to the fact that the total number of
TT stores is less now and this slightly alters the TT hits
of our benchmark.

bench: 4983262
2012-12-01 15:13:08 +01:00
Marco Costalba a5ea3a202e Don't read eval from TT anymore
Rely fully on eval cache. Note that we still save eval
info to TT, this is not needed at this moment and will be
removed in future patches. We keep it so to have a "non
functional change" patch.

No functional change.
2012-12-01 15:01:00 +01:00
Marco Costalba 69de670353 Retire eval optimization from null search
Simplify things preparing for further changes.

bench: 4983282
2012-12-01 14:48:13 +01:00
Marco Costalba 4e5d834e8e Add eval cache infrastructure
With this patch series we want to introduce a per-thread
evaluation cache to store node evaluation and do not
rely anymore on the TT table for this.

This patch just introduces the infrastructure.

No functional change.
2012-12-01 14:01:20 +01:00
Marco Costalba 4502917665 Don't double check for move legality
In case of a RootNode or a SpNode move has
been already checked for legality so we can
skip a redundant check.

Spotted by Frank Genot.

No functional change.
2012-11-27 20:05:03 +01:00
Marco Costalba 5af8179647 Update bestValue when futility pruning
In qsearch we should update the bestValue as we do
in case of futilityValue < beta, also when pruning
moves with non-positive see.

Spotted by Lucas Braesch

Bench: 5695710
2012-11-26 16:13:36 +01:00
Marco Costalba 55df3fa2d7 Avoid spamming the GUI in multipv search
Send the PV lines to GUI only once at the end of the
PV search loop or just in case of long searches.

We need to sync also sending of "currmove" info to
avoid sending info on current move without first
informing the GUI on the PV line we are searching on.

No functional change.
2012-11-18 11:46:37 +01:00
Marco Costalba 8367cf15da Triviality in move_gives_check()
It seems even a bit faster, although handling of the special
cases is not the hot path.

No functional change.
2012-11-17 13:08:04 +01:00
Marco Costalba 942989939a Better document fail-high condition
At this point we have already verified (value > alpha)
and this implies, in case of a non-PV node, where search
window size is zero, that value >= beta.

This is not so self-evident, so document the code with
an assert condition.

No functional change.
2012-11-17 12:50:10 +01:00
Marco Costalba 239d7b3fd1 Don't access std::cout from Position::print()
Let the caller to decide where to redirect (cout or cerr) the
ASCII representation of the position. Rename the function to
reflect this.

Renamed also from_fen() and to_fen() to set() and fen() respectively.

No functional change.
2012-11-11 13:30:20 +01:00
Marco Costalba 116234d6c6 Restore old BOUND_EXACT logic in qsearch
In case a PvNode node has a static evaluation above alpha but
no available moves we want to flag the node as BOUND_EXACT,
not as BOUND_UPPER as is currently.

The behaviour was recently introduced with patch d471c49700
of 3/10/2012

Spotted by Hongzhi Cheng.

bench: 5558464
2012-11-11 11:52:11 +01:00
Marco Costalba c45a4e0b48 Revert "Don't prune discovered checks"
Both Lucas re-test and Jean-Francois confirrm it
is a regression.

Here Jean-Francois's results after 3600 games :
Score of 96d3b1c92b vs 3b87314: 690 - 729 - 2181 [0.495] 3600
ELO: -3.86 +- 99%: 14.94 95%: 11.35
LOS: 15.03%
Wins: 690 Losses: 729 Draws: 2181 Total: 3600

Bench: 5404066
2012-11-11 11:20:39 +01:00
Marco Costalba 96d3b1c92b Don't prune discovered checks
Don't prune and eventually extend check moves of type
DISCO_CHECK (pun intended, Lucas will understand :-) ).

Patch from Lucas Braesch that has also tested it:

Result: 879-661-2137, score=52.96%, LOS=100.00% (time 10"+0.1")

I have started a verification test right now.

bench: 6004966
2012-11-07 18:29:56 +01:00
Marco Costalba 3b87314331 Previous patch test results
From Jean-Francois's

Final result after 5000 games :
Score of c581b7e vs a878312: 1163 - 970 - 2867 [0.519] 5000
ELO: 13.35 +- 99%: 12.71 95%: 9.65
LOS: 100.00%
Wins: 1163 Losses: 970 Draws: 2867 Total: 5000

From me

After 3266 games at 20"+0,05
Score of c581b7e vs a878312: 612 - 607 - 2047

So no regression at longer TC and perhaps a little gain at
fast TC.
2012-11-07 18:07:03 +01:00
Marco Costalba c581b7ea36 Another attempt at evaluation shortcut
In this case we try a rather drastic
approach: we simply don't futility prune
in qsearch when arriving from a null move.

So we save evaluating and also save to mess
with eval margins at all because margin is used
only in futility.

Also accuracy should not be affected, actually it
improves because we don't prune anything anymore.

bench: 5404066
2012-11-05 16:12:28 +01:00
Marco Costalba a5b1f4774f Temporary revert previous patch
Performs well at very short TC of 40/4+0.05 (courtesy of Jean-Francois):
Wins: 2503 Losses: 2146 Draws: 5581 Total: 10230 +12 ELO

But is poor at longer TC of 20"+0.05
Wins: 321 Losses: 373 Draws: 1141 Total: 1808 -10 ELO

The patch was clearly a tradoff between speed and accuracy and
the most interesting part of it are test results that can be
commented as follows:

- A short TC is very sensible to any speed increase
- A longer TC is more sensible to accuracy and less to speed

So a patch that does not change speed is suitable to be tested at
short TC, while a speed/accuracy compromise patch is IMO better to
be tested at longer TC to verify loss of accuracy can be tolerated.

In this case the revert is only temporary. We will come back again
once we will be able to preserve the evaluation margin.

bench: 5809010
2012-11-05 07:41:28 +01:00
Marco Costalba 37e9802411 Skip evaluate() call after a null move
Reuse the evaluation of the parent with inverted sign and
set margin to zero (this is an hack!).

This is done only in qsearch where almost 15% of calls are
from a null move. In normal search the number of nodes where

(ss-1)->currentMove == MOVE_NULL

is almost zero and so there is no need of using this trick.

The big advantage of this patch is a speed-up due to skipped
evaluate() calls, that are very costly.

Functionality is of course affected and we will need to proper
test it later. For now we just register a 3-4% speed up.

Suggested by Hongzhi Cheng.

bench: 5051328
2012-11-04 10:48:14 +01:00
Marco Costalba a87831230d Correctly fix "break from split" patch
In case split cut-offs we return with still
some moves to go but we really want to break
out from the loop now.

No functional change.
2012-11-03 17:09:18 +01:00
Marco Costalba dd5b3086f5 Relax constrain in prevents_threat()
When testing if a move blocks the threat path there is no
reason to require the threat to be a slider. Indeed threat
can be a double pawn push like in this example:

r1bq1rk1/ppp1np1p/4n1p1/3p4/3P2Q1/2P1B3/PPBN2PP/R4RK1 w - - 0 16

Where white's move Rf6 blocks the threat f5.

As a nice side effect we can retire the now useless helper
piece_is_slider().

This patch kicks in only very rare cases, indeed the bench is
still the same!

bench: 5809010
2012-11-03 15:57:20 +01:00
Marco Costalba 47f988f05f Sync connected_threat() and yields_to_threat()
Rename stuff so to sync as much as posisble the
two related functions.

No functional change.
2012-11-03 15:36:12 +01:00
Marco Costalba 07989712af Don't 'break' upon returning from split()
There is no guarantee that split() consumes all the node's
moves. Indeed split() can return without performing any job
for instance because MAX_SPLITPOINTS_PER_THREAD is reached
or becuase no available threads are found (this latter case
is much more common).

So search must continue in those cases and we cannot force
exiting from move's loop.

Bug introduced by 1ac417edb8 of 5/10/2012

Spotted by Frank Genot.

No functional change.
2012-11-03 14:54:38 +01:00
Marco Costalba d0d69a5358 Remove a redundant condition in connected_moves()
If a previous move attacks the king (with the piece
of the threat move removed) then must be a discovered
check, otherwise it means that first move gave check
and we were not able to do a null move.

Also renamed stuff to better document the function's
context.

No functional change.
2012-11-03 14:24:29 +01:00
Marco Costalba 972de506a0 Relax constrain in connected_moves()
When testing if a piece is moving through the squares
vacated by a previous move there is no reason to require
the piece to be a slider, indeed we can have a double
pawn push like in this example:

r1q2rk1/2p1bppp/2Pp4/pN5b/Q1P1p3/4B2P/PP1R1PP1/1K5R w - - 3 18

Where black's move f5 is connected to previous move Be7 that
frees the path.

Or we can have a castle move:

r1bqkb1r/pppp1ppp/2n1pn2/1B6/4P3/2N2N2/PPPP1PPP/R1BQK2R b KQkq - 5 1

Where a previous move Bb5 allows the white to castle king side.

This time patch is mine ;-)

new bench: 5809010
2012-11-03 13:34:04 +01:00
Marco Costalba ed1574e46c Reformat connected_moves()
Prepare code for the next patch that
will affect functionality.

No functional change.
2012-11-03 13:27:44 +01:00
Marco Costalba 52f55179a8 Fix an off-by-one bug in multi pv print
We send to GUI multi-pv info after each cycle,
not just once at the end of the PV loop. This is
because at high depths a single root search can
be very slow and we want to update the gui as
soon as we have a new PV score.

Idea is good but implementation is broken because
sort() takes as arguments a pointer to the first
element and one past the last element.

So fix the bug and rename sort arguments to better
reflect their meaning.

Another hit by Hongzhi Cheng.  Impressive!

No functional change.
2012-11-03 00:36:40 +01:00
Marco Costalba bbdf9e4737 Fix a condition in connected_moves()
When checking if the moving piece p1 in a previous
move m1 defends the destination square of a move m2
we have to use the occupancy with the from square of
m2 removed so to take in account the case in which
f2 will block an x-ray attack from p1.

For instance in this position:
r2k3r/p1pp1pb1/qn3np1/1N2P3/1p3P2/2B5/PPP3QP/R3K2R b KQ - 1 9

The move eXf6 is connected to the previous move Bc3 that
defends the destination square f6.

With this patch we have about 10% more moves detected as
'connected'. Anyhow the absolute number is very low, about
4000 more moves out of 6M nodes searched.

Another issue spotted by Hongzhi "Hawk Eye" Cheng ;-)

new bench: 5757373
2012-11-02 17:18:38 +01:00
Marco Costalba 94ecdef8ac Micro-optimize pop_lsb() for 64bit case
On Intel, perhaps due to 'lea' instruction this way of
zeroing the lsb of *b seems faster than a shift+negate.

On perft (where any speed difference is magnified) I
got a 6% speed up on my Intel i5 64bit.

Suggested by Hongzhi Cheng.

No functional change.
2012-11-02 12:11:49 +01:00
Marco Costalba e3b0327812 Fix a warning under MSVC
Compiler complies that 'cnt' is initialized but
unused (in !CheckThreeFold case). Moving the
definition of 'cnt'out of the loop  seems to do
the trick.

No functional change.
2012-11-02 11:43:23 +01:00
Marco Costalba c039103b31 Pass InCheck as template parameter of qsearch()
Instead of use a variable so to resolve many conditions
already at compile time. In quiesce is also where we
have most of the InCheck nodes and is one of the most
performance critical code paths.

Speed up of 1.5% with Clang and 1% with gcc

Suggested by Hongzhi Cheng.

No functional change.
2012-11-01 18:45:38 +01:00
Marco Costalba fe1cbe2638 Use correct occupancy in connected_threat()
When checking if a move defends the threatened piece
we correctly remove from the occupancy bitboard the
moved piece. This patch removes from the occupancy also
the threatening piece so to consider the cases of moves
that defend the threatened piece x-raying through the
threat move.

As example in this position:
r3k2r/p1ppqp2/Bn4p1/3p1n2/4P1N1/5Q1P/PPP2P1P/R3K2R w KQkq - 1 10

The threat black move is dxe4. With this patch we include
(and so don't prune) white's Bb7 that would be pruned otherwise.

The number of affected position is very low, around 1% of cases,
so we don't expect ELO changes, neverthless this is the logical
and natural thing to do.

Patch suggested by Hongzhicheng.

new bench: 5323798
2012-10-30 20:27:07 +01:00
Marco Costalba 4e31c39a64 Retire move_attacks_square()
There is only one call site. This patch is a
preparation for the next one that will affect
functionality.

No functional change.
2012-10-30 20:03:35 +01:00
Marco Costalba 13f90a30ef Get rid of ReducedStateInfo struct
ReducedStateInfo is a redundant struct that is also
prone to errors, indeed must be updated any time is
updated StateInfo. It is a trick to partial copy a
StateInfo object in do_move().

This patch takes advantage of builtin macro offsetof()
to directly calculate the number of quad words to copy.
Note that we still use memcpy to do the actual job of
copying the (48 bytes) of data.

Idea by Richard Vida.

No functional and no performance change.
2012-10-29 08:07:17 +01:00
Marco Costalba ad1941fd00 Creative formatting in uci.cpp
Have some fun breaking the indentation rules :-)

No functional change.
2012-10-28 11:28:14 +01:00
Gary Linscott cee6336515 Detect drawish KQKP endings
Based off of the rules from the wikipedia page,
here: http://en.wikipedia.org/wiki/Queen_versus_pawn_endgame.

bench does not change: 5312693 but patch is real, has been
tested on specific positions.
2012-10-28 10:17:57 +01:00
Marco Costalba b3d030294b Reformat check_is_dangerous()
And shuffle some code at search.cpp tail.

No functional change.
2012-10-27 15:07:20 +02:00
Marco Costalba cd80762c13 Use std::stack instead of fixed size array
Only in not performance critical code like pretty_pv(),
otherwise continue to use the good old C-style arrays
like in extract/insert PV where I have done some code
refactoring anyhow.

No functional change.
2012-10-27 13:31:23 +02:00
Marco Costalba 00a853950f Fix broken uci notation for promotions
Silly typo (introduced in e304db9d1e) completely
messed up move notation in case of promotions causing
"Illegal move" warning in cutechess-cli.

Reported by Jörg Oster.

No functional change.
2012-10-26 16:06:47 +02:00
Marco Costalba 4c7a71a44b Fix asserts due to TT access races
In multi-threads runs with debug on we experience some
asserts due to the fact that TT access is intrinsecally
racy and its contents cannot be always trusted so must
be validated before to be used and this is what the
patch does.

No functional case.
2012-10-26 12:41:12 +02:00
Marco Costalba c594b989c0 Extend full 3 fold detection to PvNodes
And restore old behaviour of not returning from a RootNode
without updating RootMoves[].

Also renamed is_draw() template parameters to reflect a
'positive' logic (Check instead of Skip) that is easier
to follow.

New bench: 5312693
2012-10-26 11:56:33 +02:00
Marco Costalba 71f37ac1aa Merge pull request #34 from jromang/repetition
Improve 3 fold repetition detection
2012-10-26 02:01:10 -07:00
Jean-Francois Romang 77c91ac1ba Full three fold repetition detection only at root node 2012-10-25 15:57:44 +02:00
Jean-Francois Romang 5436d98fc5 Enable true 3 fold detection in search 2012-10-25 06:54:05 +00:00
Jean-Francois Romang 0587c5b605 Allow full repetition detection
Based on sshivaji 6ee19aa5389ce60181907ba53bbb50642f2d5657 commit
2012-10-25 06:28:55 +00:00
Marco Costalba c1f4000426 Fix an assert when we stop the search
When signal 'stop' is raised we return bestValue
that could be still set at -VALUE_INFINITE and
this triggers an assert. Fix it by returning
a value we know for sure is not +-VALUE_INFINITE.

Reported by 平岡拓也 Hiraoka.

No functional change.
2012-10-25 00:07:59 +02:00
Marco Costalba 22715259a0 Rename RootPosition and shuffle think()
Just slightly code reshuffles, noting interesting here...

No functional change.
2012-10-24 15:01:39 +02:00
Marco Costalba 4eda653a56 Drop Chess960 and UCIMultiPV globals and rename MultiPV
No functional change.
2012-10-24 14:31:33 +02:00
Marco Costalba e8e5b9f537 Wrap in a class Skill Level code
Note that the actual pickup is done in the class
d'tor so to be sure it is always triggered, even
in case of a sudden exit due to a 'stop' signal.

No functional change.
2012-10-24 12:52:29 +02:00
Marco Costalba 9c2b3faec4 Shuffle aspiration window loop
No functional change.
2012-10-24 11:35:53 +02:00
Marco Costalba 70b1b79264 Retire refine_eval()
Inline its content and better comment what it does through
some renaming.

No functional change.
2012-10-22 10:03:53 +02:00
Marco Costalba 5fc8b27db9 Don't copy a full Position object in print()
Function move_to_san() requires the Position to be
passed by referenced because a do/undo move is done
inside the function to detect a possible mate and to
add to the san string the corresponding '#' suffix.

Instead of passing a copy of current position pass
directly the original position object after const
casting it. This has the advantage to avoid a costly
Position copy, on the down side a bench test could
report different searched nodes if print(move) is
used, due to the additionals do_move() calls.

No functional change.
2012-10-22 00:55:16 +02:00
Marco Costalba dbbbd3880c Don't need to init board[] with NO_PIECE
Now that NO_PIECE == 0 the common memset() will
do the work.

No functional change.
2012-10-22 00:38:12 +02:00
Marco Costalba e40b06a050 Change NO_PIECE value and shrink PieceValue[]
This requires changing color_of() definition.

No functional change.
2012-10-21 11:50:56 +02:00
Marco Costalba e304db9d1e Use self-describing constants instead of numbers
And remove now useless comments.

No functional change.
2012-10-21 11:16:21 +02:00
Marco Costalba 6b909b2343 Move RootColor from Eval to Search
No functional change.
2012-10-21 09:12:02 +02:00
Marco Costalba 55bd27b8f0 Contempt factor: use DrawValue also in case of stealmates
Spotted by Jörg Oster.

No functional change (when contempt factor is not used).
2012-10-20 11:02:37 +02:00
Marco Costalba 1018474853 Fix compatibility with old Windows 95 and 98
Report and patches by bnemias.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-10-20 10:48:26 +02:00
Marco Costalba c11edd3b0b Merge pull request #33 from daylen/master
Further improve OS X compatibility

Change the minimum supported version from 10.6 to 10.0

No functional change.
2012-10-20 01:07:27 -07:00
Daylen Yang f0298862f5 Further improve compatibility when compiling on OS X
Change the minimum supported version from 10.6 to 10.0
2012-10-16 00:13:16 -07:00
Marco Costalba 739d23f2a3 Fix Makefile for PowerPC with prefetch enabled
Existing Makefile is buggy for PowerPC, it has no
SSE, yet it is given it if Prefetch is enabled,
because it isn't ARMv7.

Patch from Matthew Brades.

No functional change.
2012-10-15 01:13:41 +02:00
Marco Costalba 3aa2d6db18 Document why is safe ttValue == VALUE_NONE
We can have ttValue == VALUE_NONE when we use a TT
slot to just save a position static evaluation, but
in this case we also save DEPTH_NONE so to avoid
using the ttValue in search. This happens to work,
but due to a number of lucky and tricky cases that
we now documnet through a bunch of asserts and a
little change to value_from_tt() that has no real
effect but clarifing the code.

No functional change.
2012-10-14 12:47:16 +02:00
Marco Costalba 6a75291ab1 Set TT size to 32 MB during 'bench'
On some platforms 128 MB of RAM for TT is too much,
so run 'bench' with the default 32 MB size.

No functional change although of course now 'bench'
reports a different number: 5545018
2012-10-14 09:02:17 +02:00
Marco Costalba eb1a4f11fa Move all Contempt Factor code to search.cpp
Where it is used.

No functional change.
2012-10-13 14:49:01 +02:00
Marco Costalba d023be5a21 Retire BitCount8Bit[] table
Use popcount() instead in the only calling place.
It is used only at initialization so there is no
speed regression and anyhow even initialization
itself is not slowed down: magic bitboard setup
stays around 175 msec on my slow 32bit Core Duo.

No functional change.
2012-10-13 14:24:01 +02:00
Marco Costalba c2cd75843e Use new 64 bit De Bruijn BitScan
Allows to sync 32 and 64 bits implementations.

Idea by Kim Walisch, reported by Gerd Isenberg:
http://talkchess.com/forum/viewtopic.php?t=45554

No functional change.
2012-10-13 13:45:45 +02:00
Marco Costalba 189b6fc270 Retire can_return_tt() and rewirte TT-hit code
Simplify the code and doing this introduce a couple
of (very small) functional changes:

- Always compare to depth even in "mate value" condition
- TT cut-off in qsearch also in case of PvNode, as in search

Verified against regression with 2500 games at 30"+0.05
on 2 threads: 451 - 444 - 1602

Functional changed: new bench is 5544977
2012-10-13 11:45:14 +02:00
Marco Costalba 6c8663341e Scale contempt factor to zero at endgame
Contempt Factor is more effective at opening/middle game
and seems harmful at endgame phase. See:

http://chessprogramming.wikispaces.com/Contempt+Factor

http://web.archive.org/web/20070707023203/www.brucemo.com/compchess/programming/contempt.htm

Therefore we scale down the contempt factor while going
on with the game so to reach zero at endgame phase.

No functional change.
2012-10-13 10:40:38 +02:00
Marco Costalba aba152ea3a Fix a minor bug in search
As Joona says: "The problem is that when doing full
window search (-VALUE_INFINITE, VALUE_INFINITE), and
pruning all the moves will return fail low which is
mate score, because only clause touching alpha is
"mate distance pruning". So we are returning mate score
although we are just pruning all the moves. In reality
there probably is no mate in sight.

Bug spotted and fixed by Joona.
2012-10-11 21:12:43 +02:00
Jean-Francois Romang 7f9ebf8e86 ARM lsb/msb assembly
Implement lsb/msb using armv7 assembly instructions.
msb is the easiest one, using a gcc intrinsic that generates
code using the ARM's clz instruction. lsb is also using this
clz instruction, but with the help of ARM's 'rbit' (bit
reversing) instruction. This leads to a >2% speed gain.

I also renamed 'arm-32' to the more meaningfull 'armv7' in the Makefile

No functional change.
2012-10-11 21:01:52 +02:00
Jean-Francois Romang 4e7da9be3d Introduce arm-32 ARCH in Makefile
No functional change.
2012-10-11 07:34:48 +02:00
Marco Costalba 46c01b5083 Retire is_dangerous() and inline its content
No functional change.
2012-10-10 08:13:36 +02:00
Marco Costalba dae843d4d6 Rearrange prefetch code
No functional change.
2012-10-08 11:43:47 +02:00
Marco Costalba 78fe0cfb8d Merge pull request #29 from RyanTaker/patch-3
Add Contempt Factor to Polyglot.ini
2012-10-07 15:36:08 -07:00
RyanTaker d77d555c72 Add Contempt Factor in Polyglot
The contempt factor was previously not included in polyglot.ini
2012-10-07 09:49:55 -07:00
Marco Costalba bbaef048cd Sync qsearch with search
Port to qsearch() the same changes we recently
added to search().

Overall this search refactoring series shows
almost 2% speed up on gcc compile.

No functional change.
2012-10-07 13:15:41 +02:00
Daylen Yang 7e2d49368d Improve compatibility with older versions of Mac OS X
Use the -mmacosx-version-min flag to support older versions of Mac OS X
(like version 10.6 and 10.7) when compiled on a machine running version
10.8.
2012-10-06 17:56:12 -07:00
Marco Costalba 954fc950d9 Fix POPCNT support on mingw 64
When using asm 'popcnt' instruction the given
operand registers must be of the same type.

No functional change.
2012-10-06 13:01:44 +02:00
Marco Costalba d777c4d789 Fix mingw compile with ARCH=x86-64
When using the Makefile (as for the mingw case),
IS_64BIT and USE_BSFQ are already set with
ARCH=x86-64 and do not need to be redefined.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-10-06 12:34:09 +02:00
Marco Costalba cedbd3332a Fix Contempt Factor implementation
First disable Contempt Factor during analysis, then
calculate the modified draw score from the point of
view of the player, so from the point of view of
RootPosition color.

Thanks to Ryan Taker for suggesting the fixes.

No functional change.
2012-10-06 10:12:34 +02:00
Marco Costalba ff9ca3e76e Fix fallouts from previous patch
These kind of arch specific code is really nasty
to make it right becuase you need to verify on
all the platforms.

Now should compile properly also on ARM

Reported by Jean-Francois.

No functional change.
2012-10-06 09:09:27 +02:00
Jean-Francois Romang b8948e84b8 Allow prefetching on non-x86 platforms with gcc
In particular on ARM processors. Original patch by
Jean-Francois, sligtly modified by me to preserve
the meaning of NO_PREFETCH flag.

Verified with gcc, clang and icc that prefetch instruction
is correctly created.

No functional change.
2012-10-06 00:43:16 +02:00
Marco Costalba 1ac417edb8 Retire futility_move_count()
And remove (bestValue < beta) condition from
moves loop.

No functional change.
2012-10-05 18:24:38 +02:00
Marco Costalba d471c49700 Rewrite search best value update
A simplification and also a small speed-up of
about 1% mainly due to reducing calls to
thisThread->cutoff_occurred().

Worst case split point recovering time after a
cut-off occurred is limited to 3 msec on my slow
PC, and usually is below 1 msec, so it seems safe
to remove the cutoff_occurred() check.

No functional change.
2012-10-05 13:53:05 +02:00
Marco Costalba c9f9262a49 Add experimental contempt factor
This is very crude and very basic: simply in case
of a draw for repetition or 50 moves rule return
a negative score instead of zero according to the
contempt factor (in centipawns). If contempt is
positive engine will try to avoid draws (to use
with weaker opponents), if negative engine will
try to draw. If zero (default) there are no changes.

No functional change.
2012-10-05 08:28:23 +02:00
Marco Costalba bd7a0d4ce4 Retire EasyMoveMargin
Use a value related to PawnValue instead.

This is a different patch from previous one because
could affect game play and skill levels, although
in a mostly unmeasurable way. Indeed thresold has
been raised so easy move is a bit harder to trigger
and skill level is a bit more prone to blunders.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-10-03 18:59:24 +02:00
Marco Costalba 561eb34aea Don't hide thresolds values
Show the real value in the code, not hide it
behind a variable name, especially when there
is only once occurence.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-10-03 18:56:36 +02:00
Marco Costalba 4c91dbc28e Further push singular extension
Extend for an extra half-ply in case the node is (probably)
going to fail high. In this case the added overhead is limited.

A novelity is the way this patch has been tested: Always in
self-play but with a much longer TC to allow the singular
extension to fully kick in and also (my impression) to have
less noisy results.

Ater 1015 games on my QUAD at 60"+0.05
Mod vs Orig 173 - 150 - 692 ELO +8
2012-10-02 06:39:27 +02:00
Marco Costalba 1b6b711c44 Further rearrange search()
No functional change.
2012-10-01 10:44:04 +02:00
Marco Costalba faaa1f1116 Don't push on the stack 200KB in one go
This could be a limit on some platforms (as it seems
to be in Native Client). Patch from a SF fork on github:

https://github.com/ccherng/Stockfish/commit/47374afd6fdfabd9de183a7a67d645daad45fb21

No functional change.
2012-09-30 11:35:14 +02:00
Marco Costalba ed0fb0b05f Add support for node limited search
Handle also the SMP case. This has been quite tricky, not
trivial to enforce the node limit in SMP case becuase
with "helpful master" concept we can have recursive split
points and we cannot lock them all at once so there is the
risk of counting the same nodes more than once.

Anyhow this patch should be race free and counted nodes are
correct.

No functional change.
2012-09-30 10:19:22 +02:00
Marco Costalba e5463eb3ae Skip some useless initializations in search()
And rearrange a bit the initialization code. Still
some polishing to do, but it is a first step.

No functional change.
2012-09-29 23:12:39 +02:00
Marco Costalba d53c928261 Don't need to early check PV moves for legality
As long as isPvMove (renamed to pvMove) is set after
legality check, we can postpone legality even in PV case.

Patch aligns the PV case with the common non-pv one.

No functional change.
2012-09-29 18:05:49 +02:00
Marco Costalba bc8f5fe0bf Drop a magic in book.cpp
Mask out move's spacial flags without relying
on internal Move representation.

No functional change.
2012-09-22 11:19:10 +02:00
Marco Costalba 9204a60dbb Tweaks to bitcount functions
Seems even a bit faster now (almost 1% in 32bit case).

No functional change.
2012-09-22 10:59:33 +02:00
Marco Costalba 1cb2722c95 Restore development version 2012-09-22 00:37:18 +02:00
Marco Costalba 3caeabf73b Stockfish 2.3.1
Stockfish bench signature is: 5423738
2012-09-22 00:20:44 +02:00
Gary Linscott fdbe8006e0 Bonus for rook/queen attacking pawns on same rank
Patch and tuning by Gary Linscott from an idea of Ryan Taker.

Double tested by Gary:

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

And by me:

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

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

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

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

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

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

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

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

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

Workaround using an anonymous namespace instead.

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

abs(bestValue) >= VALUE_KNOWN_WIN

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

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

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

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

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

No functional change.

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

Also bit of reformat while there.

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

On Clang there is even a slight speed up.

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

Suggested by Rein Halbersma.

No functional change.

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

No functional change.

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

Suggested by Rein Halbersma.

No functional change.

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

No functional change.

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

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

No functional change.

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

Spotted by Joona.

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

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

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

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

No funtional change.

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

Found by Clang with -Weverything option.

No functional change.

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

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

Suggested by Rein Halbersma.

No functional change.

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

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

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

No functional change.

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

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

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

Also some reformatting while there.

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

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

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

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

No functional change.

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

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

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

The fix is much simpler than the explanation :-)

Reported by Quocvuong82.

No functional change.

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

The fix is to reset failbit upon exiting.

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

This will be used by next patch.

No functional change.

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

Suggested by Dan Honeycutt.

No functional change.

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

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

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

No functional change.

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

No functional change.

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

Ported back by Portfish. No functional change.

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

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

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

gives the error:

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

Here RootMoves is:

 std::vector<RootMove> RootMoves;

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

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

 typedef value_type* iterator;

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

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

is that the stlport iterator type is allowed.

Report and patch by Peter Osterlund.

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

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

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

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

Suggestion and performance tests by Daylen Yang.

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

Patch suggested by Balint Pfliegel.

No functional change.

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

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

Bug spotted and tracked down by Balint Pfliegel

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

Spotted by Blint Pfliegel.

No functional change.

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

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

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

No functional change.

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

   slavesMask == allSlavesMask

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

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

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

No functional change.

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

This avoids some more wasted reparenting.

No functional chnage.

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

Of course also the usual renaming while there :-)

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

Bug reported by Daylen.

No functional changes.

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

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

No functional change.

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

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

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

No functional change.

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

Following are the nps on a bench:

Position
454165
454838
455433

tls
441046
442767
442767

ms (Win)
450521
447510
451105

ms (pthread)
422115
422115
424276

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

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

Somewhat surprisingly we don't have any speed
regression !

No functional change.

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

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

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

No functional change.

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

Bug introduced in adb71b8096

No functional change.

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

Also the usual renaming while there.

No functional change and no speed regression.

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

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

No functional change.

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

No functional change.

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

Found by Othello1984.

No functional change.

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

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

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

Thanks to Otello1984 to reporting this.

No functional change.

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

No functional change.

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

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

Spotted by Justin Blanchard.

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

No functional change.

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

No functional change.

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

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

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

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

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

No functional change.

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

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

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

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

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

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

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

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

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

[Patch series from Gary, little edited by me]

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

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

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

No functional change.

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

Suggested by Rein Halbersma.

No functional change.

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

No functional change.

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

No functional change.

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

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

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

Bug recently introduced by 2ef5b4066e.

No functional change.

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

Speed up of about 1%.

No functional change.

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

Bug reported by several users.

No functional change.

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

No functional change.

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

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

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

No functional change.

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

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

No functional change

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

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

No functional change.

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

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

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

No functional change.

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

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

No functional change.

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

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

Idea from Critter although implementation is different.

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

This incredible bug was here since Glaurung times !

Spotted by Kiriakos.

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

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

No functional change.

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

Reshuffle stuff in thread.cpp while there.

No functional change.

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

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

No functional change.

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

No functional change.

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

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

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

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

Fix the race lock protecting splitPoint access.

No functional change.

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

Spotted by Richard Vida.

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

No functional change.

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

Bug reported by Ajedrecista.

No functional change.

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

No functional change.

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

No functional change.

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

No functional change.

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

Spotted by Trung Tu.

No functional change.

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

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

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

No functional change.

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

Workaround found by Jim Ablett.

No functional change.

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

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

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

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

Regression reported by Benigno Hernandez.

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

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

No functional change.

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

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

No functional change.

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

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

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

Also a bit of renaming while there.

No functional change.

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

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

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

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

No functional change.

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

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

No functional change.

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

No functional change.

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

Spotted by Louis Zulli.

No functional change.

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

No functional change.

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

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

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

No functional changes.

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

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

stockfish bench signature is: 5447426

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

No functional change.

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

No functional change.

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

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

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

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

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

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

No functional change.

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

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

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

No functional change.

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

No functional change.

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

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

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

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

No functional change.

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

Reported by Richard Lloyd.

No functional change.

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

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 18:09:25 +01:00
Marco Costalba 78e6b361c5 Stockfish 2.2.1
Hopefully fixed the "lose on time" issue.

stockfish bench signature is: 5457475

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 17:01:40 +01:00
Marco Costalba aa392c366e Extra time management safety
Further increase safety against time losses. After this
change (tested on LittleBlitzer and cutechess) I had no
more time losses at 2" and 1"+0.02 TC both on Windows
and Linux on more than 10000 games.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 13:24:37 +01:00
Marco Costalba f80c50bcdd Try hard not to lose on time
We try hard not to lose on time even under extreme
time pressure. We achieve this through 3 different but
coordinated steps:

    1) Increase max frequency of timer events

    2) Quickly return after a stop signal

    3) Take in account timer resolution

With these SF played under LittleBlitzer at 1"+0.02 and 3"+0
without losing on time even one game.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 01:41:45 +01:00
Marco Costalba d282cf6964 Avoid a race at thread creation
Before creating main thread we set its do_sleep flag to true,
then thread is created and it will go to sleep in main_loop()
after resetting do_sleep.

But if after the setting of do_sleep and before its resetting
the UI thread calls start_thinking() it will not wait on:

  if (!asyncMode)
      while (!main.do_sleep)
          cond_wait(&sleepCond, &main.sleepLock);

as it should but will immediately return before the main thread has
started the search. This very rare race show itself during bench,
when the first position is erroneusly skipped so that bench node count
results of 5309038 instead of the correct 5457475.

The patch is somewhat tricky, but is simple and it works!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 21:31:50 +01:00
Marco Costalba b1fcfe4c5d Streamline generation of MV_NON_EVASION
Small speed-up of 3% in perft.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 19:18:38 +01:00
Marco Costalba 30418a3cfc Fix a warning under gcc
Locals left and right shadow two same named
variables in the std::ifstream base class.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-03 19:01:47 +01:00
Marco Costalba cb1709ef5e Revert cond_signal() fix
It seems it yields to missing wake-up events with the
result of SF loosing on time as reported by many people.

So revert the patch and use a more robust approach: assume
there can be spurious wake ups events and make the code to
work also in those cases.

While debugging I found that WaitForSingleObject() had wrong
parameter 0 instead of INFINITE yielding to a crash while
exiting under Windows, strangely unnoticed til now.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-02 16:44:04 +01:00
Marco Costalba 67338e6f32 Big renaming of move's helpers
The aim is to have shorter names without losing
readibility but, if possible, increasing it.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-02 12:03:54 +01:00
Marco Costalba 8300ab149c Simplify Book APIs
Retire open(), close() and name() from public visibility
and greately simplify the code. It is amazing how much
can be squeezed out of an already mature code !

No functional change

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

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2011-12-31 15:44:00 +01:00
48 changed files with 5162 additions and 5807 deletions
+74
View File
@@ -0,0 +1,74 @@
### Overview
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program and requires some UCI-compatible GUI
(e.g. XBoard with PolyGlot, eboard, 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 it.
This version of Stockfish supports up to 64 CPUs, but has not been
tested thoroughly with more than 4. The program tries to detect the
number of CPUs on your computer and sets 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 eight threads, it is
recommended to raise the value of the *Min Split Depth* UCI parameter to 7.
### Files
This distribution of Stockfish consists of the following files:
* Readme.md, 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 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter.
### Opening books
This version of Stockfish has 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 *Book File*
UCI parameter.
### Compiling it yourself
On Unix-like systems, it should be possible to compile Stockfish
directly from the source code with the included Makefile.
Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
instruction, big-endian machines such as Power PC, and other platforms.
In general it is recommended to run `make help` to see a list of make
targets with corresponding descriptions. When not using Makefile to
compile (for instance with Microsoft MSVC) you need to manually
set/unset some switches in the compiler command line; see file *types.h*
for a quick reference.
### 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*
-79
View File
@@ -1,79 +0,0 @@
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, 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 32 CPUs, but has not been tested
thoroughly with more than 4. The program tries to detect the number of
CPUs on your computer and set the number of search threads 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 eight threads, it is recommended to raise the value of
"Min Split Depth" UCI parameter to 7.
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 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.
Stockfish has support for 32 or 64 bits CPUS, big-endian machines, like
Power PC, hardware POPCNT instruction and other platforms.
In general is recommended to run 'make help' to see a list of make targets
with corresponding descriptions. When not using Makefile to compile, for
instance with Microsoft MSVC, you need to manually set/unset in the compiler
command line some swicthes, see file types.h for a quick reference.
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.
+1 -1
View File
@@ -1,4 +1,3 @@
[PolyGlot]
EngineDir = .
@@ -19,6 +18,7 @@ Use Search Log = false
Search Log Filename = SearchLog.txt
Book File = book.bin
Best Book Move = false
Contempt Factor = 0
Mobility (Middle Game) = 100
Mobility (Endgame) = 100
Passed Pawns (Middle Game) = 100
+101 -90
View File
@@ -1,6 +1,6 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
# Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,11 +20,18 @@
### Section 1. General Configuration
### ==========================================================================
### Establish the operating system name
UNAME = $(shell uname)
### Executable name
EXE = stockfish
### Installation dir definitions
PREFIX = /usr/local
# Haiku has a non-standard filesystem layout
ifeq ($(UNAME),Haiku)
PREFIX=/boot/common
endif
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
@@ -32,7 +39,7 @@ PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files
OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
material.o misc.o move.o movegen.o movepick.o pawns.o position.o \
material.o misc.o movegen.o movepick.o notation.o pawns.o position.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o
### ==========================================================================
@@ -47,11 +54,11 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = yes/no --- -DBIGENDIAN --- big/little-endian byte order
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only
# with GCC and ICC 64-bit)
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
@@ -68,40 +75,20 @@ ifeq ($(ARCH),general-64)
arch = any
os = any
bits = 64
bigendian = no
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),general-32)
arch = any
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-64)
arch = any
os = any
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-32)
arch = any
os = any
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
# x86-section
@@ -109,40 +96,51 @@ ifeq ($(ARCH),x86-64)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
sse = yes
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
endif
ifeq ($(ARCH),x86-32)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
sse = yes
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
#arm section
ifeq ($(ARCH),armv7)
arch = armv7
os = any
bits = 32
prefetch = yes
bsfq = yes
popcnt = no
sse = no
endif
# osx-section
@@ -150,40 +148,40 @@ ifeq ($(ARCH),osx-ppc-64)
arch = ppc64
os = osx
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),osx-ppc-32)
arch = ppc
os = osx
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),osx-x86-64)
arch = x86_64
os = osx
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
sse = yes
endif
ifeq ($(ARCH),osx-x86-32)
arch = i386
os = osx
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
sse = yes
endif
@@ -223,6 +221,15 @@ ifeq ($(COMP),icc)
profile_clean = icc-profile-clean
endif
ifeq ($(COMP),clang)
comp=clang
CXX=clang++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
@@ -235,18 +242,34 @@ ifeq ($(comp),mingw)
endif
ifeq ($(comp),icc)
CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
CXXFLAGS += -wd383,981,1418,1419,1476,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif
ifeq ($(comp),clang)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.0
endif
### 3.3 General linker settings
LDFLAGS = -lpthread $(EXTRALDFLAGS)
LDFLAGS = $(EXTRALDFLAGS)
ifeq ($(comp),mingw)
LDFLAGS += -static-libstdc++ -static-libgcc
endif
### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw)
# Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
LDFLAGS += -lpthread
endif
endif
ifeq ($(os),osx)
LDFLAGS += -arch $(arch)
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.0
endif
### 3.4 Debugging
@@ -268,6 +291,10 @@ ifeq ($(optimize),yes)
CXXFLAGS += -mdynamic-no-pic
endif
endif
ifeq ($(arch),armv7)
CXXFLAGS += -fno-gcse
endif
endif
ifeq ($(comp),mingw)
@@ -281,6 +308,20 @@ ifeq ($(optimize),yes)
CXXFLAGS += -O3
endif
endif
ifeq ($(comp),clang)
### -O4 requires a linker that supports LLVM's LTO
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
endif
### 3.6. Bits
@@ -288,38 +329,37 @@ ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 Endianess
ifeq ($(bigendian),yes)
CXXFLAGS += -DBIGENDIAN
endif
### 3.8 prefetch
### 3.7 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
ifeq ($(sse),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
endif
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.9 bsfq
### 3.8 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.10 popcnt
### 3.9 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -msse3 -DUSE_POPCNT
endif
### 3.11 Link Time Optimization, it works since gcc 4.5 but not on mingw.
### 3.10 Link Time Optimization, it works since gcc 4.5 but not on mingw.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
GCC_MAJOR := `gcc -dumpversion | cut -f1 -d.`
GCC_MINOR := `gcc -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
ifeq ($(optimize),yes)
GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.`
GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
endif
@@ -337,7 +377,6 @@ help:
@echo ""
@echo "build > Build unoptimized version"
@echo "profile-build > Build PGO-optimized version"
@echo "double-profile-build > Build PGO-optimized version with and without popcnt support"
@echo "strip > Strip executable"
@echo "install > Install executable"
@echo "clean > Clean up"
@@ -347,22 +386,22 @@ help:
@echo ""
@echo "x86-64 > x86 64-bit"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support"
@echo "x86-32 > x86 32-bit excluding old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit"
@echo "osx-x86-32 > x86-Mac OS X 32 bit"
@echo "armv7 > ARMv7 32 bit"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo "bigendian-64 > unspecified 64-bit with bigendian byte order"
@echo "bigendian-32 > unspecified 32-bit with bigendian byte order"
@echo ""
@echo "Supported comps:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@echo ""
@echo "Non-standard targets:"
@echo ""
@@ -398,34 +437,6 @@ profile-build:
@echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
double-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
@echo ""
@echo "Step 0/6. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/6. Building executable for benchmark (popcnt disabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 6/6. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
@echo ""
strip:
strip $(EXE)
@@ -457,10 +468,10 @@ config-sanity:
@echo "arch: '$(arch)'"
@echo "os: '$(os)'"
@echo "bits: '$(bits)'"
@echo "bigendian: '$(bigendian)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo "sse: '$(sse)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@@ -472,14 +483,14 @@ config-sanity:
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc"
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
@test "$(os)" = "any" || test "$(os)" = "osx"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(bigendian)" = "yes" || test "$(bigendian)" = "no"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw"
@test "$(sse)" = "yes" || test "$(sse)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
$(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
@@ -531,7 +542,7 @@ icc-profile-clean:
hpux:
$(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \
LDFLAGS="" \
all
+41 -31
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,16 +19,17 @@
#include <fstream>
#include <iostream>
#include <istream>
#include <vector>
#include "misc.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
using namespace std;
using namespace Search;
static const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
@@ -58,34 +59,42 @@ static const char* Defaults[] = {
/// format (defaults are the positions defined above) and the type of the
/// limit value: depth (default), time in secs or number of nodes.
void benchmark(int argc, char* argv[]) {
void benchmark(const Position& current, istream& is) {
string token;
Search::LimitsType limits;
vector<string> fens;
LimitsType limits;
int time;
int64_t nodes = 0;
// Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128";
string threads = argc > 3 ? argv[3] : "1";
string valStr = argc > 4 ? argv[4] : "12";
string fenFile = argc > 5 ? argv[5] : "default";
string valType = argc > 6 ? argv[6] : "depth";
string ttSize = (is >> token) ? token : "32";
string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "12";
string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth";
Options["Hash"] = ttSize;
Options["Threads"] = threads;
Options["OwnBook"] = false;
TT.clear();
if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
if (limitType == "time")
limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms
else if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str());
else if (limitType == "mate")
limits.mate = atoi(limit.c_str());
else
limits.maxDepth = atoi(valStr.c_str());
limits.depth = atoi(limit.c_str());
if (fenFile != "default")
if (fenFile == "default")
fens.assign(Defaults, Defaults + 16);
else if (fenFile == "current")
fens.push_back(current.fen());
else
{
string fen;
ifstream file(fenFile.c_str());
@@ -93,7 +102,7 @@ void benchmark(int argc, char* argv[]) {
if (!file.is_open())
{
cerr << "Unable to open file " << fenFile << endl;
exit(EXIT_FAILURE);
return;
}
while (getline(file, fen))
@@ -102,34 +111,35 @@ void benchmark(int argc, char* argv[]) {
file.close();
}
else
fens.assign(Defaults, Defaults + 16);
time = system_time();
int64_t nodes = 0;
Search::StateStackPtr st;
Time::point elapsed = Time::now();
for (size_t i = 0; i < fens.size(); i++)
{
Position pos(fens[i], false, 0);
Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (valType == "perft")
if (limitType == "perft")
{
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
size_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
else
{
Threads.start_thinking(pos, limits, vector<Move>(), false);
nodes += RootPosition.nodes_searched();
Threads.start_thinking(pos, limits, vector<Move>(), st);
Threads.wait_for_think_finished();
nodes += Search::RootPos.nodes_searched();
}
}
time = system_time() - time;
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero'
cerr << "\n==========================="
<< "\nTotal time (ms) : " << time
<< "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << int(nodes / (time / 1000.0)) << endl;
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
}
+112 -210
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,254 +18,156 @@
*/
#include <cassert>
#include <vector>
#include "bitboard.h"
#include "types.h"
namespace {
enum Result {
RESULT_UNKNOWN,
RESULT_INVALID,
RESULT_WIN,
RESULT_DRAW
};
struct KPKPosition {
Result classify_knowns(int index);
Result classify(int index, Result db[]);
private:
void from_index(int index);
Result classify_white(const Result db[]);
Result classify_black(const Result db[]);
Bitboard wk_attacks() const { return StepAttacksBB[W_KING][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[B_KING][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[W_PAWN][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq = 196608
const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in a way that minimizes number of iterations:
//
// bit 0- 5: white king square (from SQ_A1 to SQ_H8)
// bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15);
}
enum Result {
INVALID = 0,
UNKNOWN = 1,
DRAW = 2,
WIN = 4
};
inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
struct KPKPosition {
operator Result() const { return res; }
Result classify_leaf(unsigned idx);
Result classify(const std::vector<KPKPosition>& db)
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
private:
template<Color Us> Result classify(const std::vector<KPKPosition>& db);
Color us;
Square bksq, wksq, psq;
Result res;
};
} // namespace
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
assert(file_of(wpsq) <= FILE_D);
unsigned idx = index(us, bksq, wksq, wpsq);
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
}
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
void Bitbases::init_kpk() {
int index = compute_index(wksq, bksq, wpsq, stm);
unsigned idx, repeat = 1;
std::vector<KPKPosition> db(IndexMax);
return KPKBitbase[index / 32] & (1 << (index & 31));
}
// Initialize db with known win / draw positions
for (idx = 0; idx < IndexMax; idx++)
db[idx].classify_leaf(idx);
void kpk_bitbase_init() {
Result db[IndexMax];
KPKPosition pos;
int index, bit, repeat = 1;
// Initialize table
for (index = 0; index < IndexMax; index++)
db[index] = pos.classify_knowns(index);
// Iterate until all positions are classified (30 cycles needed)
// Iterate through the positions until no more of the unknown positions can be
// changed to either wins or draws (15 cycles needed).
while (repeat)
for (repeat = index = 0; index < IndexMax; index++)
if ( db[index] == RESULT_UNKNOWN
&& pos.classify(index, db) != RESULT_UNKNOWN)
for (repeat = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (index = 0; index < IndexMax / 32; index++)
for (bit = 0; bit < 32; bit++)
if (db[32 * index + bit] == RESULT_WIN)
KPKBitbase[index] |= (1 << bit);
// Map 32 results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax; idx++)
if (db[idx] == WIN)
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
}
namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
Result KPKPosition::classify_leaf(unsigned idx) {
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
us = Color((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
assert(file_of(wpsq) <= FILE_D);
// Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq
|| (StepAttacksBB[KING][wksq] & bksq)
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
return res = INVALID;
int p = file_of(wpsq) + 4 * (rank_of(wpsq) - 1);
int r = stm + 2 * bksq + 128 * wksq + 8192 * p;
if (us == WHITE)
{
// Immediate win if pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7
&& wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
return res = WIN;
}
// Immediate draw if is stalemate or king captures undefended pawn
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
return res = DRAW;
assert(r >= 0 && r < IndexMax);
return r;
return res = UNKNOWN;
}
void KPKPosition::from_index(int index) {
template<Color Us>
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
int s = index >> 13;
sideToMove = Color(index & 1);
blackKingSquare = Square((index >> 1) & 63);
whiteKingSquare = Square((index >> 7) & 63);
pawnSquare = make_square(File(s & 3), Rank((s >> 2) + 1));
}
Result KPKPosition::classify_knowns(int index) {
from_index(index);
// Check if two pieces are on the same square
if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return RESULT_INVALID;
// Check if a king can be captured
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| (bit_is_set(pawn_attacks(), blackKingSquare) && sideToMove == WHITE))
return RESULT_INVALID;
// The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured.
if ( rank_of(pawnSquare) == RANK_7
&& sideToMove == WHITE
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N)))
return RESULT_WIN;
// Check for known draw positions
// White to Move: If one move leads to a position classified as WIN, the result
// of the current position is WIN. If all moves lead to positions classified
// as DRAW, the current position is classified DRAW otherwise the current
// position is classified as UNKNOWN.
//
// Case 1: Stalemate
if ( sideToMove == BLACK
&& !(bk_attacks() & ~(wk_attacks() | pawn_attacks())))
return RESULT_DRAW;
// Black to Move: If one move leads to a position classified as DRAW, the result
// of the current position is DRAW. If all moves lead to positions classified
// as WIN, the position is classified WIN otherwise the current position is
// classified UNKNOWN.
// Case 2: King can capture pawn
if ( sideToMove == BLACK
&& bit_is_set(bk_attacks(), pawnSquare) && !bit_is_set(wk_attacks(), pawnSquare))
return RESULT_DRAW;
Result r = INVALID;
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
// Case 3: Black king in front of white pawn
if ( blackKingSquare == pawnSquare + DELTA_N
&& rank_of(pawnSquare) < RANK_7)
return RESULT_DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( whiteKingSquare == pawnSquare + DELTA_N
&& blackKingSquare == pawnSquare + DELTA_N + DELTA_N + DELTA_N
&& rank_of(pawnSquare) < RANK_5
&& sideToMove == WHITE)
return RESULT_DRAW;
// Case 5: Stalemate with rook pawn
if ( blackKingSquare == SQ_A8
&& file_of(pawnSquare) == FILE_A)
return RESULT_DRAW;
return RESULT_UNKNOWN;
}
Result KPKPosition::classify(int index, Result db[]) {
from_index(index);
db[index] = (sideToMove == WHITE ? classify_white(db) : classify_black(db));
return db[index];
}
Result KPKPosition::classify_white(const Result db[]) {
// If one move leads to a position classified as RESULT_WIN, the result
// of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = wk_attacks();
while (b)
r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)]
: db[index(~Us, pop_lsb(&b), wksq, psq)];
if (Us == WHITE && rank_of(psq) < RANK_7)
{
s = pop_1st_bit(&b);
r = db[compute_index(s, blackKingSquare, pawnSquare, BLACK)];
Square s = psq + DELTA_N;
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
}
// Pawn moves
if (rank_of(pawnSquare) < RANK_7)
{
s = pawnSquare + DELTA_N;
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
// Double pawn push
if (rank_of(s) == RANK_3 && r != RESULT_INVALID)
{
s += DELTA_N;
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
}
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
if (Us == WHITE)
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
else
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
}
Result KPKPosition::classify_black(const Result db[]) {
// If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the position is classified as RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
Result r;
// King moves
b = bk_attacks();
while (b)
{
s = pop_1st_bit(&b);
r = db[compute_index(whiteKingSquare, s, pawnSquare, WHITE)];
if (r == RESULT_DRAW)
return RESULT_DRAW;
if (r == RESULT_UNKNOWN)
unknownFound = true;
}
return unknownFound ? RESULT_UNKNOWN : RESULT_WIN;
}
}
} // namespace
+158 -179
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,190 +23,179 @@
#include "bitboard.h"
#include "bitcount.h"
#include "misc.h"
#include "rkiss.h"
Bitboard RMasks[64];
Bitboard RMagics[64];
Bitboard* RAttacks[64];
int RShifts[64];
CACHE_LINE_ALIGNMENT
Bitboard BMasks[64];
Bitboard BMagics[64];
Bitboard* BAttacks[64];
int BShifts[64];
Bitboard RMasks[SQUARE_NB];
Bitboard RMagics[SQUARE_NB];
Bitboard* RAttacks[SQUARE_NB];
unsigned RShifts[SQUARE_NB];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard BMasks[SQUARE_NB];
Bitboard BMagics[SQUARE_NB];
Bitboard* BAttacks[SQUARE_NB];
unsigned BShifts[SQUARE_NB];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8];
Bitboard ThisAndNeighboringFilesBB[8];
Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64];
Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ThisAndAdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
int SquareDistance[64][64];
int SquareDistance[SQUARE_NB][SQUARE_NB];
namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn_32 = 0x783A9B23;
CACHE_LINE_ALIGNMENT
int BSFTable[64];
Bitboard RookTable[0x19000]; // Storage space for rook attacks
Bitboard BishopTable[0x1480]; // Storage space for bishop attacks
int MS1BTable[256];
Square BSFTable[SQUARE_NB];
Bitboard RTable[0x19000]; // Storage space for rook attacks
Bitboard BTable[0x1480]; // Storage space for bishop attacks
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]);
}
typedef unsigned (Fn)(Square, Bitboard);
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
/// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
FORCE_INLINE unsigned bsf_index(Bitboard b) {
void print_bitboard(Bitboard b) {
for (Rank r = RANK_8; r >= RANK_1; r--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? "X " : " ");
std::cout << "|\n";
// Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
b ^= (b - 1);
return Is64Bit ? (b * DeBruijn_64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
}
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if !defined(USE_BSFQ)
#if defined(IS_64BIT) && !defined(USE_BSFQ)
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
Square first_1(Bitboard b) {
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
}
Square pop_lsb(Bitboard* b) {
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
*b = bb & (bb - 1);
return BSFTable[bsf_index(bb)];
}
#elif !defined(USE_BSFQ)
Square msb(Bitboard b) {
Square first_1(Bitboard b) {
b ^= (b - 1);
uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
}
unsigned b32;
int result = 0;
// Use type-punning
union b_union {
if (b > 0xFFFFFFFF)
{
b >>= 32;
result = 32;
}
Bitboard b;
struct {
#if defined (BIGENDIAN)
uint32_t h;
uint32_t l;
#else
uint32_t l;
uint32_t h;
#endif
} dw;
};
b32 = unsigned(b);
Square pop_1st_bit(Bitboard* bb) {
if (b32 > 0xFFFF)
{
b32 >>= 16;
result += 16;
}
b_union u;
Square ret;
if (b32 > 0xFF)
{
b32 >>= 8;
result += 8;
}
u.b = *bb;
if (u.dw.l)
{
ret = Square(BSFTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783A9B23) >> 26]);
u.dw.l &= (u.dw.l - 1);
*bb = u.b;
return ret;
}
ret = Square(BSFTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783A9B23) >> 26]);
u.dw.h &= (u.dw.h - 1);
*bb = u.b;
return ret;
return (Square)(result + MS1BTable[b32]);
}
#endif // !defined(USE_BSFQ)
/// bitboards_init() initializes various bitboard arrays. It is called during
/// Bitboards::print() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void Bitboards::print(Bitboard b) {
sync_cout;
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File file = FILE_A; file <= FILE_H; file++)
std::cout << "| " << (b & (file | rank) ? "X " : " ");
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl;
}
/// Bitboards::init() initializes various bitboard arrays. It is called during
/// program initialization.
void bitboards_init() {
void Bitboards::init() {
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
for (int k = 0, i = 0; i < 8; i++)
while (k < (2 << i))
MS1BTable[k++] = i;
for (int i = 0; i < 64; i++)
BSFTable[bsf_index(1ULL << i)] = Square(i);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = 1ULL << s;
ClearMaskBB[s] = ~SetMaskBB[s];
}
ClearMaskBB[SQ_NONE] = ~0ULL;
SquareBB[s] = 1ULL << s;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
for (int i = 1; i < 8; i++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
FileBB[i] = FileBB[i - 1] << 1;
RankBB[i] = RankBB[i - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
for (File f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(file_of(s));
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(file_of(s));
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (int i = 0; i < 64; i++)
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
{
Bitboard b = 1ULL << i;
b ^= b - 1;
b ^= b >> 32;
BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i;
}
else
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (int d = 1; d < 8; d++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
@@ -218,97 +207,85 @@ void bitboards_init() {
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[make_piece(c, pt)][s], to);
if (is_ok(to) && square_distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to;
}
init_magic_bitboards(ROOK, RAttacks, RMagics, RMasks, RShifts);
init_magic_bitboards(BISHOP, BAttacks, BMagics, BMasks, BShifts);
Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0);
RookPseudoAttacks[s] = rook_attacks_bb(s, 0);
QueenPseudoAttacks[s] = queen_attacks_bb(s, 0);
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
if (PseudoAttacks[QUEEN][s1] & s2)
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
set_bit(&BetweenBB[s1][s2], s);
BetweenBB[s1][s2] |= s;
}
}
namespace {
Bitboard sliding_attacks(PieceType pt, Square sq, Bitboard occupied) {
Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) {
Square deltas[][4] = { { DELTA_N, DELTA_E, DELTA_S, DELTA_W },
{ DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW } };
Bitboard attacks = 0;
Square* delta = (pt == ROOK ? deltas[0] : deltas[1]);
Bitboard attack = 0;
for (int i = 0; i < 4; i++)
{
Square s = sq + delta[i];
while (square_is_ok(s) && square_distance(s, s - delta[i]) == 1)
for (Square s = sq + deltas[i];
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
s += deltas[i])
{
set_bit(&attacks, s);
attack |= s;
if (bit_is_set(occupied, s))
if (occupied & s)
break;
s += delta[i];
}
}
return attacks;
return attack;
}
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
Bitboard magic;
Bitboard pick_random(RKISS& rk, int booster) {
// Values s1 and s2 are used to rotate the candidate magic of a
// quantity known to be the optimal to quickly find the magics.
int s1 = booster & 63, s2 = (booster >> 6) & 63;
while (true)
{
magic = rk.rand<Bitboard>();
magic = (magic >> s1) | (magic << (64 - s1));
magic &= rk.rand<Bitboard>();
magic = (magic >> s2) | (magic << (64 - s2));
magic &= rk.rand<Bitboard>();
if (BitCount8Bit[(mask * magic) >> 56] >= 6)
return magic;
}
Bitboard m = rk.rand<Bitboard>();
m = (m >> s1) | (m << (64 - s1));
m &= rk.rand<Bitboard>();
m = (m >> s2) | (m << (64 - s2));
return m & rk.rand<Bitboard>();
}
// init_magic_bitboards() computes all rook and bishop magics at startup.
// Magic bitboards are used to look up attacks of sliding pieces. As reference
// see chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]) {
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, index, booster;
int i, size, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = (pt == ROOK ? RookTable : BishopTable);
attacks[SQ_A1] = table;
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
@@ -320,15 +297,15 @@ namespace {
// all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attacks(pt, s, 0) & ~edges;
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attacks bitboard in reference[].
// store the corresponding sliding attack bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attacks(pt, s, b);
reference[size++] = sliding_attack(deltas, s, b);
b = (b - masks[s]) & masks[s];
} while (b);
@@ -342,7 +319,9 @@ namespace {
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
do {
magics[s] = pick_random(masks[s], rk, booster);
do magics[s] = pick_random(rk, booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
@@ -351,14 +330,14 @@ namespace {
// effect of verifying the magic.
for (i = 0; i < size; i++)
{
index = (pt == ROOK ? rook_index(s, occupancy[i])
: bishop_index(s, occupancy[i]));
Bitboard& attack = attacks[s][index(s, occupancy[i])];
if (!attacks[s][index])
attacks[s][index] = reference[i];
else if (attacks[s][index] != reference[i])
if (attack && attack != reference[i])
break;
assert(reference[i] != 0);
attack = reference[i];
}
} while (i != size);
}
+156 -137
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,64 +23,76 @@
#include "types.h"
extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
extern Bitboard RankBB[8];
extern Bitboard InFrontBB[2][8];
namespace Bitboards {
extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65];
void init();
void print(Bitboard b);
extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64];
extern uint64_t RMagics[64];
extern int RShifts[64];
extern Bitboard RMasks[64];
extern Bitboard* RAttacks[64];
extern uint64_t BMagics[64];
extern int BShifts[64];
extern Bitboard BMasks[64];
extern Bitboard* BAttacks[64];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
/// Functions for testing whether a given bit is set in a bitboard, and for
/// setting and clearing bits.
inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
}
inline void set_bit(Bitboard* b, Square s) {
*b |= SetMaskBB[s];
namespace Bitbases {
void init_kpk();
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
}
inline void clear_bit(Bitboard* b, Square s) {
*b &= ClearMaskBB[s];
CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[SQUARE_NB];
extern Bitboard RMagics[SQUARE_NB];
extern Bitboard* RAttacks[SQUARE_NB];
extern unsigned RShifts[SQUARE_NB];
extern Bitboard BMasks[SQUARE_NB];
extern Bitboard BMagics[SQUARE_NB];
extern Bitboard* BAttacks[SQUARE_NB];
extern unsigned BShifts[SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL;
/// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) {
return b & SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
return b ^= SquareBB[s];
}
inline Bitboard operator|(Bitboard b, Square s) {
return b | SquareBB[s];
}
inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s];
}
/// Functions used to update a bitboard after a move. This is faster
/// then calling a sequence of clear_bit() + set_bit()
/// more_than_one() returns true if in 'b' there is more than one bit set
inline Bitboard make_move_bb(Square from, Square to) {
return SetMaskBB[from] | SetMaskBB[to];
}
inline void do_move_bb(Bitboard* b, Bitboard move_bb) {
*b ^= move_bb;
inline bool more_than_one(Bitboard b) {
return b & (b - 1);
}
@@ -104,19 +116,19 @@ inline Bitboard file_bb(Square s) {
}
/// neighboring_files_bb takes a file as input and returns a bitboard representing
/// all squares on the neighboring files.
/// adjacent_files_bb takes a file as input and returns a bitboard representing
/// all squares on the adjacent files.
inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f];
inline Bitboard adjacent_files_bb(File f) {
return AdjacentFilesBB[f];
}
/// this_and_neighboring_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and neighboring files.
/// this_and_adjacent_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and adjacent files.
inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f];
inline Bitboard this_and_adjacent_files_bb(File f) {
return ThisAndAdjacentFilesBB[f];
}
@@ -135,71 +147,30 @@ inline Bitboard in_front_bb(Color c, Square s) {
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.
/// between_bb returns a bitboard representing all squares between two squares.
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal,
/// 0 is returned.
#if defined(IS_64BIT)
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
return unsigned(((occ & RMasks[s]) * RMagics[s]) >> RShifts[s]);
}
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
return unsigned(((occ & BMasks[s]) * BMagics[s]) >> BShifts[s]);
}
#else // if !defined(IS_64BIT)
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
Bitboard b = occ & RMasks[s];
return unsigned(int(b) * int(RMagics[s]) ^ int(b >> 32) * int(RMagics[s] >> 32)) >> RShifts[s];
}
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
Bitboard b = occ & BMasks[s];
return unsigned(int(b) * int(BMagics[s]) ^ int(b >> 32) * int(BMagics[s] >> 32)) >> BShifts[s];
}
#endif
inline Bitboard rook_attacks_bb(Square s, Bitboard occ) {
return RAttacks[s][rook_index(s, occ)];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard occ) {
return BAttacks[s][bishop_index(s, occ)];
}
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
/// squares_between returns a bitboard representing all squares between
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
/// on the same line, file or diagonal, EmptyBoardBB is returned.
inline Bitboard squares_between(Square s1, Square s2) {
inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2];
}
/// squares_in_front_of takes a color and a square as input, and returns a
/// bitboard representing all squares along the line in front of the square,
/// from the point of view of the given color. Definition of the table is:
/// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s)
/// forward_bb takes a color and a square as input, and returns a bitboard
/// representing all squares along the line in front of the square, from the
/// point of view of the given color. Definition of the table is:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
inline Bitboard squares_in_front_of(Color c, Square s) {
return SquaresInFrontMask[c][s];
inline Bitboard forward_bb(Color c, Square s) {
return ForwardBB[c][s];
}
/// passed_pawn_mask takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s)
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_adjacent_files_bb(s)
inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
@@ -209,7 +180,7 @@ inline Bitboard passed_pawn_mask(Color c, Square s) {
/// attack_span_mask takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
/// AttackSpanMask[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) {
return AttackSpanMask[c][s];
@@ -221,7 +192,7 @@ inline Bitboard attack_span_mask(Color c, Square s) {
inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ( SetMaskBB[s1] | SetMaskBB[s2] | SetMaskBB[s3]);
& ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
}
@@ -229,48 +200,96 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return bit_is_set(0xAA55AA55AA55AA55ULL, s) ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
return BlackSquares & s ? BlackSquares : ~BlackSquares;
}
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard
/// representing all squares attacked by Pt (bishop or rook) on the given square.
template<PieceType Pt>
FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) {
Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks;
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
if (Is64Bit)
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occ) & unsigned(Masks[s]);
unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occ) {
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
}
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if defined(USE_BSFQ)
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square first_1(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
FORCE_INLINE Square lsb(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
}
#else
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard dummy;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
return (Square) dummy;
FORCE_INLINE Square msb(Bitboard b) {
unsigned long index;
_BitScanReverse64(&index, b);
return (Square) index;
}
#endif
FORCE_INLINE Square pop_1st_bit(Bitboard* b) {
const Square s = first_1(*b);
*b &= ~(1ULL<<s);
# elif defined(__arm__)
FORCE_INLINE int lsb32(uint32_t v) {
__asm__("rbit %0, %1" : "=r"(v) : "r"(v));
return __builtin_clz(v);
}
FORCE_INLINE Square msb(Bitboard b) {
return (Square) (63 - __builtin_clzll(b));
}
FORCE_INLINE Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
}
# else
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard index;
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
FORCE_INLINE Square msb(Bitboard b) {
Bitboard index;
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
# endif
FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b);
*b &= *b - 1;
return s;
}
#else // if !defined(USE_BSFQ)
extern Square first_1(Bitboard b);
extern Square pop_1st_bit(Bitboard* b);
extern Square msb(Bitboard b);
extern Square lsb(Bitboard b);
extern Square pop_lsb(Bitboard* b);
#endif
extern void print_bitboard(Bitboard b);
extern void bitboards_init();
#endif // !defined(BITBOARD_H_INCLUDED)
+24 -31
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,8 +33,8 @@ enum BitCountType {
};
/// Determine at compile time the best popcount<> specialization according if
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// use hardware popcnt instruction when available.
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count
/// and if hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
@@ -44,44 +44,38 @@ template<BitCountType> inline int popcount(Bitboard);
template<>
inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
b *= 0x0101010101010101ULL;
return int(b >> 56);
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
return (b * 0x0101010101010101ULL) >> 56;
}
template<>
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL;
return int(b >> 60);
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
return (b * 0x1111111111111111ULL) >> 60;
}
template<>
inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits
v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits
v *= 0x01010101; // mul is fast on amd procs
return int(v >> 24);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F;
return (v * 0x01010101) >> 24;
}
template<>
inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v += w; // 0-8 in 4 bits
v *= 0x11111111;
return int(v >> 28);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
return ((v + w) * 0x11111111) >> 28;
}
template<>
@@ -90,7 +84,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
assert(false);
return int(b != 0); // Avoid 'b not used' warning
return b != 0; // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
@@ -102,9 +96,8 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#else
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
__asm__("popcnt %1, %0" : "=r" (b) : "r" (b));
return b;
#endif
}
+124 -176
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,13 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/
#include <algorithm>
#include <cassert>
#include <iostream>
@@ -35,8 +35,26 @@ using namespace std;
namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with highest byte first (regardless of size).
// The entries are ordered according to the key in ascending order.
struct Entry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
// Random numbers from PolyGlot, used to compute book hash keys
const Key PolyGlotRandoms[781] = {
const union {
Key PolyGlotRandoms[781];
struct {
Key psq[12][64]; // [piece][square]
Key castle[4]; // [castle right]
Key enpassant[8]; // [file]
Key turn;
} Zobrist;
} PG = {{
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -298,233 +316,163 @@ namespace {
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
};
}};
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms + 0;
const Key* ZobCastle = PolyGlotRandoms + 768;
const Key* ZobEnPassant = PolyGlotRandoms + 772;
const Key* ZobTurn = PolyGlotRandoms + 780;
// polyglot_key() returns the PolyGlot hash key of the given position
Key polyglot_key(const Position& pos) {
// Piece offset is calculated as 64 * (PolyPiece ^ 1) where
// PolyPiece is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
const int PieceOfs[] = { 0, 64, 192, 320, 448, 576, 704, 0,
0, 0, 128, 256, 384, 512, 640 };
// book_key() builds up a PolyGlot hash key out of a position
uint64_t book_key(const Position& pos) {
uint64_t result = 0;
Bitboard b = pos.occupied_squares();
Key key = 0;
Bitboard b = pos.pieces();
while (b)
{
Square s = pop_1st_bit(&b);
result ^= ZobPiece[PieceOfs[pos.piece_on(s)] + s];
Square s = pop_lsb(&b);
Piece p = pos.piece_on(s);
// PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s];
}
if (pos.can_castle(WHITE_OO))
result ^= ZobCastle[0];
b = pos.can_castle(ALL_CASTLES);
if (pos.can_castle(WHITE_OOO))
result ^= ZobCastle[1];
if (pos.can_castle(BLACK_OO))
result ^= ZobCastle[2];
if (pos.can_castle(BLACK_OOO))
result ^= ZobCastle[3];
while (b)
key ^= PG.Zobrist.castle[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE)
result ^= ZobEnPassant[file_of(pos.ep_square())];
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
result ^= ZobTurn[0];
key ^= PG.Zobrist.turn;
return result;
}
}
/// Book c'tor. Make random number generation less deterministic, for book moves
Book::Book() : bookSize(0) {
for (int i = abs(system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>();
}
/// Book destructor. Be sure file is closed before we leave.
Book::~Book() {
close();
}
/// Book::close() closes the file only if it is open, otherwise the call fails
/// and the failbit internal state flag is set.
void Book::close() {
if (bookFile.is_open())
bookFile.close();
bookName = "";
bookSize = 0;
}
/// Book::open() opens a book file with a given name
void Book::open(const string& fileName) {
// Close old file before opening the new
close();
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary |ios::ate);
// Silently return when asked to open a non-exsistent file
if (!bookFile.is_open())
return;
// Get the book size in number of entries, we are already at the file end
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
if (!bookFile.good())
{
cerr << "Failed to open book file " << fileName << endl;
exit(EXIT_FAILURE);
return key;
}
// Set only if successful
bookName = fileName;
} // namespace
PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
/// converts them in a number of type T. A Polyglot book stores numbers in
/// big-endian format.
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
}
/// Book::probe() gets a book move for a given position. Returns MOVE_NONE
/// if no book move is found. If findBest is true then returns always the
/// highest rated move otherwise chooses randomly based on the move score.
/// open() tries to open a book file with the given name after closing any
/// exsisting one.
Move Book::probe(const Position& pos, bool findBest) {
bool PolyglotBook::open(const char* fName) {
if (!bookSize || !bookFile.is_open())
if (is_open()) // Cannot close an already closed file
close();
ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
return !fileName.empty();
}
/// probe() tries to find a book move for the given position. If no move is
/// found returns MOVE_NONE. If pickBest is true returns always the highest
/// rated move, otherwise randomly chooses one, based on the move score.
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE;
BookEntry entry;
unsigned scoresSum = 0, bestScore = 0, bookMove = 0;
uint64_t key = book_key(pos);
int idx = first_entry(key) - 1;
Entry e;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
Key key = polyglot_key(pos);
// Choose a book move among the possible moves for the given position
while (++idx < bookSize && (entry = read_entry(idx), entry.key == key))
seekg(find_first(key) * sizeof(Entry), ios_base::beg);
while (*this >> e, e.key == key && good())
{
scoresSum += entry.count;
best = max(best, e.count);
sum += e.count;
// Choose book move according to its score. If a move has a very
// high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
if ( RKiss.rand<unsigned>() % scoresSum < entry.count
|| (findBest && entry.count > bestScore))
bookMove = entry.move;
if (entry.count > bestScore)
bestScore = entry.count;
if ( (sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
if (!bookMove)
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow "king captures rook" representation. So in case
// book move is a promotion we have to convert to our representation, in
// all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot.
int promotion = (bookMove >> 12) & 7;
// Castling moves follow "king captures rook" representation. So in case book
// move is a promotion we have to convert to our representation, in all the
// other cases we can directly compare with a Move after having masked out
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
int pt = (move >> 12) & 7;
if (pt)
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
if (promotion)
bookMove = make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)),
PieceType(promotion + 1));
// Verify the book move is legal
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (unsigned(ml.move() & ~(3 << 14)) == bookMove) // Mask out special flags
// Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() ^ type_of(ml.move())))
return ml.move();
return MOVE_NONE;
}
/// Book::first_entry() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first (leftmost)
/// book entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
/// find_first() takes a book key as input, and does a binary search through
/// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input.
int Book::first_entry(uint64_t key) {
size_t PolyglotBook::find_first(Key key) {
int left, right, mid;
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
// Binary search (finds the leftmost entry with given key)
left = 0;
right = bookSize - 1;
size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
Entry e;
assert(left <= right);
assert(low <= high);
while (left < right)
while (low < high && good())
{
mid = (left + right) / 2;
mid = (low + high) / 2;
assert(mid >= left && mid < right);
assert(mid >= low && mid < high);
if (key <= read_entry(mid).key)
right = mid;
seekg(mid * sizeof(Entry), ios_base::beg);
*this >> e;
if (key <= e.key)
high = mid;
else
left = mid + 1;
low = mid + 1;
}
assert(left == right);
assert(low == high);
return read_entry(left).key == key ? left : bookSize;
}
/// Book::operator>>() reads sizeof(T) chars from the file's binary byte
/// stream and converts them in a number of type T.
template<typename T>
Book& Book::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + bookFile.get());
return *this;
}
/// Book::read_entry() takes an integer index, and returns the BookEntry
/// at the given index in the book file.
BookEntry Book::read_entry(int idx) {
assert(idx >= 0 && idx < bookSize);
assert(bookFile.is_open());
BookEntry e;
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
*this >> e.key >> e.move >> e.count >> e.learn;
if (!bookFile.good())
{
cerr << "Failed to read book entry at index " << idx << endl;
exit(EXIT_FAILURE);
}
return e;
return low;
}
+10 -26
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,36 +26,20 @@
#include "position.h"
#include "rkiss.h"
/// A Polyglot book is a series of "entries" of 16 bytes. All integers are
/// stored highest byte first (regardless of size). The entries are ordered
/// according to key. Lowest key first.
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
class Book {
class PolyglotBook : private std::ifstream {
public:
Book();
~Book();
void open(const std::string& fileName);
void close();
Move probe(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
PolyglotBook();
~PolyglotBook();
Move probe(const Position& pos, const std::string& fName, bool pickBest);
private:
template<typename T> Book& operator>>(T& n);
template<typename T> PolyglotBook& operator>>(T& n);
BookEntry read_entry(int idx);
int first_entry(uint64_t key);
bool open(const char* fName);
size_t find_first(Key key);
RKISS RKiss;
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS rkiss;
std::string fileName;
};
#endif // !defined(BOOK_H_INCLUDED)
+169 -93
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,19 +20,18 @@
#include <algorithm>
#include <cassert>
#include "bitboard.h"
#include "bitcount.h"
#include "endgame.h"
#include "pawns.h"
#include "movegen.h"
using std::string;
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
namespace {
// Table used to drive the defending king towards the edge of the board
// in KX vs K and KQ vs KR endgames.
const int MateTable[64] = {
const int MateTable[SQUARE_NB] = {
100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80,
@@ -45,7 +44,7 @@ namespace {
// Table used to drive the defending king towards a corner square of the
// right color in KBN vs K endgames.
const int KBNKMateTable[64] = {
const int KBNKMateTable[SQUARE_NB] = {
200, 190, 180, 170, 160, 150, 140, 130,
190, 180, 170, 160, 150, 140, 130, 140,
180, 170, 155, 140, 140, 125, 140, 150,
@@ -72,12 +71,12 @@ namespace {
string sides[] = { code.substr(code.find('K', 1)), // Weaker
code.substr(0, code.find('K', 1)) }; // Stronger
transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fen = sides[0] + char('0' + int(8 - code.length()))
+ sides[1] + "/8/8/8/8/8/8/8 w - - 0 10";
return Position(fen, false, 0).material_key();
return Position(fen, false, NULL).material_key();
}
template<typename M>
@@ -96,10 +95,12 @@ Endgames::Endgames() {
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KBBKN>("KBBKN");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
@@ -116,10 +117,8 @@ Endgames::~Endgames() {
template<EndgameType E>
void Endgames::add(const string& code) {
typedef typename eg_family<E>::type T;
map((T*)0)[key(code, WHITE)] = new Endgame<E>(WHITE);
map((T*)0)[key(code, BLACK)] = new Endgame<E>(BLACK);
map((Endgame<E>*)0)[key(code, WHITE)] = new Endgame<E>(WHITE);
map((Endgame<E>*)0)[key(code, BLACK)] = new Endgame<E>(BLACK);
}
@@ -133,19 +132,26 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
// Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide
&& !pos.checkers()
&& !MoveList<LEGAL>(pos).size()) {
return VALUE_DRAW;
}
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEndgame
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN)
|| pos.piece_count(strongerSide, ROOK)
|| pos.piece_count(strongerSide, BISHOP) > 1)
// TODO: check for two equal-colored bishops!
result += VALUE_KNOWN_WIN;
|| pos.bishop_pair(strongerSide)) {
result += VALUE_KNOWN_WIN;
}
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -158,19 +164,19 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0];
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1.
if (opposite_colors(bishopSquare, SQ_A1))
if (opposite_colors(bishopSq, SQ_A1))
{
winnerKSq = mirror(winnerKSq);
loserKSq = mirror(loserKSq);
@@ -194,21 +200,21 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq, bksq, wpsq;
Color stm;
Color us;
if (strongerSide == WHITE)
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move();
us = pos.side_to_move();
}
else
{
wksq = flip(pos.king_square(BLACK));
bksq = flip(pos.king_square(WHITE));
wpsq = flip(pos.piece_list(BLACK, PAWN)[0]);
stm = flip(pos.side_to_move());
wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
us = ~pos.side_to_move();
}
if (file_of(wpsq) >= FILE_E)
@@ -218,12 +224,10 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
wpsq = mirror(wpsq);
}
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN
+ PawnValueEndgame
+ Value(rank_of(wpsq));
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
return strongerSide == pos.side_to_move() ? result : -result;
}
@@ -236,7 +240,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
template<>
Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1);
@@ -251,24 +255,24 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
if (strongerSide == BLACK)
{
wksq = flip(wksq);
wrsq = flip(wrsq);
bksq = flip(bksq);
bpsq = flip(bpsq);
wksq = ~wksq;
wrsq = ~wrsq;
bksq = ~bksq;
bpsq = ~bpsq;
}
Square queeningSq = make_square(file_of(bpsq), RANK_1);
Square queeningSq = file_of(bpsq) | RANK_1;
Value result;
// If the stronger side's king is in front of the pawn, it's a win
if (wksq < bpsq && file_of(wksq) == file_of(bpsq))
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
result = RookValueEg - Value(square_distance(wksq, bpsq));
// If the weaker side's king is too far from the pawn and the rook,
// it's a win
else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
&& square_distance(bksq, wrsq) >= 3)
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
result = RookValueEg - Value(square_distance(wksq, bpsq));
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
@@ -293,9 +297,9 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
template<>
Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
@@ -309,9 +313,9 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
template<>
Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
@@ -324,6 +328,37 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
}
/// KQ vs KP. In general, a win for the stronger side, however, there are a few
/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can
/// be a draw, so we scale down to distance between kings only.
template<>
Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0];
Value result = QueenValueEg
- PawnValueEg
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( square_distance(loserKSq, pawnSq) == 1
&& relative_rank(weakerSide, pawnSq) == RANK_7)
{
File f = file_of(pawnSq);
if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H)
result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]);
}
return strongerSide == pos.side_to_move() ? result : -result;
}
/// KQ vs KR. This is almost identical to KX vs K: We give the attacking
/// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move
@@ -332,16 +367,16 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
template<>
Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
Value result = QueenValueEndgame
- RookValueEndgame
Value result = QueenValueEg
- RookValueEg
+ MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
@@ -352,12 +387,12 @@ template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
assert(!pos.pieces(PAWN));
Value result = BishopValueEndgame;
Value result = BishopValueEg;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
@@ -394,29 +429,29 @@ Value Endgame<KNNK>::operator()(const Position&) const {
template<>
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) >= 1);
// No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(PAWN, strongerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN);
File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]);
// All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H)
if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& !(pawns & ~file_bb(pawnFile)))
{
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8);
Square kingSq = pos.king_square(weakerSide);
if ( opposite_colors(queeningSq, bishopSq)
&& abs(file_of(kingSq) - pawnFile) <= 1)
{
// The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the
// file of the pawn(s) or the adjacent file. Find the rank of the
// frontmost pawn.
Rank rank;
if (strongerSide == WHITE)
@@ -437,6 +472,29 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
return SCALE_FACTOR_DRAW;
}
}
// All pawns on same B or G file? Then potential draw
if ( (pawnFile == FILE_B || pawnFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile))
&& pos.non_pawn_material(weakerSide) == 0
&& pos.piece_count(weakerSide, PAWN) >= 1)
{
// Get weaker pawn closest to opponent's queening square
Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns);
Square strongerKingSq = pos.king_square(strongerSide);
Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
// Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
// weaker king can stop opposing opponent's king from penetrating.
if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7
&& opposite_colors(bishopSq, weakerPawnSq)
&& square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
}
@@ -446,7 +504,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, QUEEN) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, ROOK) == 1);
@@ -455,12 +513,12 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2
&& relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4
&& (pos.pieces(ROOK, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_3)))
&& (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
&& (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3)))
&& (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN)))
{
Square rsq = pos.piece_list(weakerSide, ROOK)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
@@ -476,9 +534,9 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq = pos.king_square(strongerSide);
@@ -491,11 +549,11 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// pawn is on the left half of the board.
if (strongerSide == BLACK)
{
wksq = flip(wksq);
wrsq = flip(wrsq);
wpsq = flip(wpsq);
bksq = flip(bksq);
brsq = flip(brsq);
wksq = ~wksq;
wrsq = ~wrsq;
wpsq = ~wpsq;
bksq = ~bksq;
brsq = ~brsq;
}
if (file_of(wpsq) > FILE_D)
{
@@ -508,7 +566,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
File f = file_of(wpsq);
Rank r = rank_of(wpsq);
Square queeningSq = make_square(f, RANK_8);
Square queeningSq = f | RANK_8;
int tempo = (pos.side_to_move() == strongerSide);
// If the pawn is not too far advanced and the defending king defends the
@@ -594,9 +652,9 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
@@ -638,15 +696,15 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0);
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(PAWN, strongerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN);
// Are all pawns on the 'a' file?
if (!(pawns & ~FileABB))
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( file_of(ksq) == FILE_A
&& !in_front_bb(strongerSide, ksq) & pawns))
|| ( file_of(ksq) == FILE_A
&& !(in_front_bb(strongerSide, ksq) & pawns)))
return SCALE_FACTOR_DRAW;
}
// Are all pawns on the 'h' file?
@@ -654,8 +712,8 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( file_of(ksq) == FILE_H
&& !in_front_bb(strongerSide, ksq) & pawns))
|| ( file_of(ksq) == FILE_H
&& !(in_front_bb(strongerSide, ksq) & pawns)))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
@@ -669,10 +727,10 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
@@ -705,9 +763,9 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_DRAW;
else
{
Bitboard path = squares_in_front_of(strongerSide, pawnSq);
Bitboard path = forward_bb(strongerSide, pawnSq);
if (path & pos.pieces(KING, weakerSide))
if (path & pos.pieces(weakerSide, KING))
return SCALE_FACTOR_DRAW;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
@@ -724,10 +782,10 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
@@ -747,12 +805,12 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
{
blockSq1 = psq1 + pawn_push(strongerSide);
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
blockSq2 = file_of(psq2) | rank_of(psq1);
}
else
{
blockSq1 = psq2 + pawn_push(strongerSide);
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
blockSq2 = file_of(psq1) | rank_of(psq2);
}
switch (file_distance(psq1, psq2))
@@ -768,20 +826,20 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE;
case 1:
// Pawns on neighboring files. Draw if defender firmly controls the square
// Pawns on adjacent files. Draw if defender firmly controls the square
// in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn.
if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakerSide, BISHOP))
|| abs(r1 - r2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakerSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
@@ -799,10 +857,10 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0);
@@ -826,7 +884,7 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
@@ -847,6 +905,24 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
}
/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking
/// pawn, it's a win. Otherwise, drawn.
template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
if (forward_bb(strongerSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
return ScaleFactor(square_distance(weakerKingSq, pawnSq));
return SCALE_FACTOR_NONE;
}
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
/// the pawn, she probably has at least a draw with the pawn as well. The exception
@@ -863,14 +939,14 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Color stm = pos.side_to_move();
Color us = pos.side_to_move();
if (strongerSide == BLACK)
{
wksq = flip(wksq);
bksq = flip(bksq);
wpsq = flip(wpsq);
stm = flip(stm);
wksq = ~wksq;
bksq = ~bksq;
wpsq = ~wpsq;
us = ~us;
}
if (file_of(wpsq) >= FILE_E)
@@ -888,5 +964,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}
+18 -16
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@ enum EndgameType {
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKP, // KQ vs KP
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
@@ -57,15 +58,17 @@ enum EndgameType {
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KNPKB, // KNP vs KB
KPKP // KP vs KP
};
/// Some magic to detect family type of endgame from its enum value
/// Endgame functions can be of two types according if return a Value or a
/// ScaleFactor. Type eg_fun<int>::type equals to either ScaleFactor or Value
/// depending if the template parameter is 0 or 1.
template<bool> struct bool_to_type { typedef Value type; };
template<> struct bool_to_type<true> { typedef ScaleFactor type; };
template<EndgameType E> struct eg_family : public bool_to_type<(E > SCALE_FUNS)> {};
template<int> struct eg_fun { typedef Value type; };
template<> struct eg_fun<1> { typedef ScaleFactor type; };
/// Base and derived templates for endgame evaluation and scaling functions
@@ -79,10 +82,10 @@ struct EndgameBase {
};
template<EndgameType E, typename T = typename eg_family<E>::type>
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {}
explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {}
Color color() const { return strongerSide; }
T operator()(const Position&) const;
@@ -93,28 +96,27 @@ private:
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its operator() method that is virtual.
/// endgame function calling its operator() that is virtual.
class Endgames {
typedef std::map<Key, EndgameBase<Value>*> M1;
typedef std::map<Key, EndgameBase<ScaleFactor>*> M2;
typedef std::map<Key, EndgameBase<eg_fun<0>::type>*> M1;
typedef std::map<Key, EndgameBase<eg_fun<1>::type>*> M2;
M1 m1;
M2 m2;
M1& map(Value*) { return m1; }
M2& map(ScaleFactor*) { return m2; }
M1& map(M1::mapped_type) { return m1; }
M2& map(M2::mapped_type) { return m2; }
template<EndgameType E> void add(const std::string& code);
public:
Endgames();
~Endgames();
~Endgames();
template<typename T> EndgameBase<T>* get(Key key) {
return map((T*)0).count(key) ? map((T*)0)[key] : NULL;
}
template<typename T> T probe(Key key, T& eg)
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
};
#endif // !defined(ENDGAME_H_INCLUDED)
+242 -230
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
*/
#include <cassert>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <algorithm>
@@ -37,13 +36,13 @@ namespace {
struct EvalInfo {
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
PawnInfo* pi;
Material::Entry* mi;
Pawns::Entry* pi;
// attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][0] contains
// all squares attacked by the given color.
Bitboard attackedBy[2][8];
// attacked by a given color and piece type, attackedBy[color][ALL_PIECES]
// contains all squares attacked by the given color.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// kingRing[color] is the zone around the king which is considered
// by the king safety evaluation. This consists of the squares directly
@@ -51,25 +50,25 @@ namespace {
// squares two ranks in front of the king. For instance, if black's king
// is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6.
Bitboard kingRing[2];
Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingRing of the enemy king.
int kingAttackersCount[2];
int kingAttackersCount[COLOR_NB];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingRing of the enemy king. The
// weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
int kingAttackersWeight[2];
int kingAttackersWeight[COLOR_NB];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
// directly adjacent to the king of the given color. Pieces which attack
// more than one square are counted multiple times. For instance, if black's
// king is on g8 and there's a white knight on g5, this knight adds
// 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[2];
int kingAdjacentZoneAttacksCount[COLOR_NB];
};
// Evaluation grain size, must be a power of 2
@@ -89,7 +88,7 @@ namespace {
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0)
S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
};
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
@@ -115,7 +114,7 @@ namespace {
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and
// bishops, indexed by piece type and square (from white's point of view).
const Value OutpostBonus[][64] = {
const Value OutpostBonus[][SQUARE_NB] = {
{
// A B C D E F G H
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
@@ -135,11 +134,11 @@ namespace {
// ThreatBonus[attacking][attacked] contains threat bonuses according to
// which piece type attacks which one.
const Score ThreatBonus[][8] = {
const Score ThreatBonus[][PIECE_TYPE_NB] = {
{}, {},
{ S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
{ S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
{ S(0, 0), S(-1, 29), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK
{ S(0, 0), S( 0, 22), S(15, 49), S(15, 49), S( 0, 0), S(24, 49) }, // ROOK
{ S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
};
@@ -151,23 +150,38 @@ namespace {
#undef S
// Rooks and queens on the 7th rank (modified by Joona Kiiski)
const Score RookOn7thBonus = make_score(47, 98);
const Score QueenOn7thBonus = make_score(27, 54);
const Score BishopPinBonus = make_score(66, 11);
// Bonus for having the side to move (modified by Joona Kiiski)
const Score Tempo = make_score(24, 11);
// Rooks and queens on the 7th rank
const Score RookOn7thBonus = make_score(11, 20);
const Score QueenOn7thBonus = make_score( 3, 8);
// Rooks and queens attacking pawns on the same rank
const Score RookOnPawnBonus = make_score(10, 28);
const Score QueenOnPawnBonus = make_score( 4, 20);
// Rooks on open files (modified by Joona Kiiski)
const Score RookOpenFileBonus = make_score(43, 43);
const Score RookHalfOpenFileBonus = make_score(19, 19);
const Score RookOpenFileBonus = make_score(43, 21);
const Score RookHalfOpenFileBonus = make_score(19, 10);
// Penalty for rooks trapped inside a friendly king which has lost the
// right to castle.
const Value TrappedRookPenalty = Value(180);
// Penalty for bishop with pawns on the same coloured squares
const Score BishopPawnsPenalty = make_score(8, 12);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games.
const Score TrappedBishopA1H1Penalty = make_score(100, 100);
// Penalty for an undefended bishop or knight
const Score UndefendedMinorPenalty = make_score(25, 10);
// The SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middle game, each side is given a bonus
// based on how many squares inside this area are safe and available for
@@ -212,16 +226,16 @@ namespace {
// KingDangerTable[Color][attackUnits] contains the actual king danger
// weighted scores, indexed by color and by a calculated integer number.
Score KingDangerTable[2][128];
Score KingDangerTable[COLOR_NB][128];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing.
Score TracedScores[2][16];
Score TracedScores[COLOR_NB][16];
std::stringstream TraceStream;
enum TracedType {
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
};
// Function prototypes
@@ -248,42 +262,120 @@ namespace {
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight);
Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf);
Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
void init_safety();
double to_cp(Value v);
void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO);
void trace_row(const char* name, int idx);
}
/// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) { return do_evaluate<false>(pos, margin); }
namespace Eval {
/// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) {
return do_evaluate<false>(pos, margin);
}
/// init() computes evaluation weights from the corresponding UCI parameters
/// and setup king tables.
void init() {
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
const int MaxSlope = 30;
const int Peak = 1280;
for (int t = 0, i = 1; i < 100; i++)
{
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
KingDangerTable[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDangerTable[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
}
}
/// trace() is like evaluate() but instead of a value returns a string suitable
/// to be print on stdout with the detailed descriptions and values of each
/// evaluation term. Used mainly for debugging.
std::string trace(const Position& pos) {
Value margin;
std::string totals;
Search::RootColor = pos.side_to_move();
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
}
} // namespace Eval
namespace {
template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) {
assert(!pos.checkers());
EvalInfo ei;
Value margins[2];
Value margins[COLOR_NB];
Score score, mobilityWhite, mobilityBlack;
assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.in_check());
// Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables).
score = pos.value();
Thread* th = pos.this_thread();
// margins[] store the uncertainty estimation of position's evaluation
// that typically is used by the search for pruning decisions.
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables) and adding
// Tempo bonus. Score is computed from the point of view of white.
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
// Probe the material hash table
ei.mi = Threads[pos.thread()].materialTable.material_info(pos);
ei.mi = Material::probe(pos, th->materialTable, th->endgames);
score += ei.mi->material_value();
// If we have a specialized evaluation function for the current material
@@ -295,7 +387,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// Probe the pawn hash table
ei.pi = Threads[pos.thread()].pawnTable.pawn_info(pos);
ei.pi = Pawns::probe(pos, th->pawnsTable);
score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards
@@ -339,12 +431,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those.
if ( ei.mi->game_phase() < PHASE_MIDGAME
&& pos.opposite_colored_bishops()
&& pos.opposite_bishops()
&& sf == SCALE_FACTOR_NORMAL)
{
// Only the two bishops ?
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
&& pos.non_pawn_material(BLACK) == BishopValueMidgame)
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
{
// Check for KBP vs KB with only a single pawn that is almost
// certainly a draw or at least two pawns.
@@ -357,14 +449,13 @@ Value do_evaluate(const Position& pos, Value& margin) {
sf = ScaleFactor(50);
}
// Interpolate between the middle game and the endgame score
margin = margins[pos.side_to_move()];
Value v = scale_by_game_phase(score, ei.mi->game_phase(), sf);
Value v = interpolate(score, ei.mi->game_phase(), sf);
// In case of tracing add all single evaluation contributions for both white and black
if (Trace)
{
trace_add(PST, pos.value());
trace_add(PST, pos.psq_score());
trace_add(IMBALANCE, ei.mi->material_value());
trace_add(PAWN, ei.pi->pawns_value());
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
@@ -387,34 +478,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
return pos.side_to_move() == WHITE ? v : -v;
}
} // namespace
/// read_weights() reads evaluation weights from the corresponding UCI parameters
void read_evaluation_uci_options(Color us) {
// King safety is asymmetrical. Our king danger level is weighted by
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
const int kingDangerUs = (us == WHITE ? KingDangerUs : KingDangerThem);
const int kingDangerThem = (us == WHITE ? KingDangerThem : KingDangerUs);
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[kingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[kingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
// If running in analysis mode, make sure we use symmetrical king safety. We do this
// by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (Options["UCI_AnalyseMode"])
Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
init_safety();
}
namespace {
// init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation.
@@ -429,7 +492,7 @@ namespace {
// Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
&& pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN];
@@ -454,10 +517,10 @@ namespace {
// Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can exchange the outpost piece.
if (bonus && bit_is_set(ei.attackedBy[Us][PAWN], s))
if (bonus && (ei.attackedBy[Us][PAWN] & s))
{
if ( !pos.pieces(KNIGHT, Them)
&& !(same_color_squares(s) & pos.pieces(BISHOP, Them)))
if ( !pos.pieces(Them, KNIGHT)
&& !(same_color_squares(s) & pos.pieces(Them, BISHOP)))
bonus += bonus + bonus / 2;
else
bonus += bonus / 2;
@@ -488,16 +551,14 @@ namespace {
if (Piece == KNIGHT || Piece == QUEEN)
b = pos.attacks_from<Piece>(s);
else if (Piece == BISHOP)
b = bishop_attacks_bb(s, pos.occupied_squares() & ~pos.pieces(QUEEN, Us));
b = attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN));
else if (Piece == ROOK)
b = rook_attacks_bb(s, pos.occupied_squares() & ~pos.pieces(ROOK, QUEEN, Us));
b = attacks_bb<ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN));
else
assert(false);
// Update attack info
ei.attackedBy[Us][Piece] |= b;
// King attacks
if (b & ei.kingRing[Them])
{
ei.kingAttackersCount[Us]++;
@@ -507,7 +568,6 @@ namespace {
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
}
// Mobility
mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea));
@@ -515,20 +575,37 @@ namespace {
// Decrease score if we are attacked by an enemy pawn. Remaining part
// of threat evaluation must be done later when we have full attack info.
if (bit_is_set(ei.attackedBy[Them][PAWN], s))
if (ei.attackedBy[Them][PAWN] & s)
score -= ThreatenedByPawnPenalty[Piece];
// Otherwise give a bonus if we are a bishop and can pin a piece or
// can give a discovered check through an x-ray attack.
else if ( Piece == BISHOP
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s)
&& !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces()))
score += BishopPinBonus;
// Penalty for bishop with same coloured pawns
if (Piece == BISHOP)
score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s);
// Bishop and knight outposts squares
if ( (Piece == BISHOP || Piece == KNIGHT)
&& !(pos.pieces(PAWN, Them) & attack_span_mask(Us, s)))
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
score += evaluate_outposts<Piece, Us>(pos, ei, s);
// Queen or rook on 7th rank
if ( (Piece == ROOK || Piece == QUEEN)
&& relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8)
if ((Piece == ROOK || Piece == QUEEN) && relative_rank(Us, s) >= RANK_5)
{
score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
// Major piece on 7th rank
if ( relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8)
score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus);
// Major piece attacking pawns on the same rank
Bitboard pawns = pos.pieces(Them, PAWN) & rank_bb(s);
if (pawns)
score += (Piece == ROOK ? RookOnPawnBonus
: QueenOnPawnBonus) * popcount<Max15>(pawns);
}
// Special extra evaluation for bishops
@@ -542,7 +619,7 @@ namespace {
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
{
if (!pos.square_is_empty(s + d + pawn_push(Us)))
if (!pos.is_empty(s + d + pawn_push(Us)))
score -= 2*TrappedBishopA1H1Penalty;
else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN))
score -= TrappedBishopA1H1Penalty;
@@ -608,15 +685,24 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Bitboard b, undefendedMinors, weakEnemies;
Score score = SCORE_ZERO;
// Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them)
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
& ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors)
score += UndefendedMinorPenalty;
// Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][0];
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
if (!weakEnemies)
return SCORE_ZERO;
return score;
// Add bonus according to type of attacked enemy piece and to the
// type of attacking piece, from knights to queens. Kings are not
@@ -652,9 +738,9 @@ namespace {
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
return score;
}
@@ -670,8 +756,8 @@ namespace {
int attackUnits;
const Square ksq = pos.king_square(Us);
// King shelter
Score score = ei.pi->king_shelter<Us>(pos, ksq);
// King shelter and enemy pawns storm
Score score = ei.pi->king_safety<Us>(pos, ksq);
// King safety. This is quite complicated, and is almost certainly far
// from optimally tuned.
@@ -680,7 +766,7 @@ namespace {
{
// Find the attacked squares around the king which has no defenders
// apart from the king itself
undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][KING];
undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING];
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN]);
@@ -693,7 +779,7 @@ namespace {
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)]
- mg_value(ei.pi->king_shelter<Us>(pos, ksq)) / 32;
- mg_value(score) / 32;
// Analyse enemy's safe queen contact checks. First find undefended
// squares around the king attacked by enemy queen...
@@ -714,7 +800,7 @@ namespace {
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy rook gives check
b &= RookPseudoAttacks[ksq];
b &= PseudoAttacks[ROOK][ksq];
if (b)
{
@@ -728,7 +814,7 @@ namespace {
}
// Analyse enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]);
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
b1 = pos.attacks_from<ROOK>(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
@@ -761,8 +847,8 @@ namespace {
// value that will be used for pruning because this value can sometimes
// be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece.
score -= KingDangerTable[Us][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
score -= KingDangerTable[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]);
}
if (Trace)
@@ -788,7 +874,7 @@ namespace {
return SCORE_ZERO;
do {
Square s = pop_1st_bit(&b);
Square s = pop_lsb(&b);
assert(pos.pawn_is_passed(Us, s));
@@ -812,19 +898,19 @@ namespace {
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
// If the pawn is free to advance, increase bonus
if (pos.square_is_empty(blockSq))
if (pos.is_empty(blockSq))
{
squaresToQueen = squares_in_front_of(Us, s);
defendedSquares = squaresToQueen & ei.attackedBy[Us][0];
squaresToQueen = forward_bb(Us, s);
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy.
if ( (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them))
&& (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
if ( (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them));
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
// If there aren't enemy attacks or pieces along the path to queen give
// huge bonus. Even bigger if we protect the pawn's path.
@@ -841,7 +927,7 @@ namespace {
// Increase the bonus if the passed pawn is supported by a friendly pawn
// on the same rank and a bit smaller if it's on the previous rank.
supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(file_of(s));
supportingPawns = pos.pieces(Us, PAWN) & adjacent_files_bb(file_of(s));
if (supportingPawns & rank_bb(s))
ebonus += Value(r * 20);
@@ -856,9 +942,9 @@ namespace {
// value if the other side has a rook or queen.
if (file_of(s) == FILE_A || file_of(s) == FILE_H)
{
if (pos.non_pawn_material(Them) <= KnightValueMidgame)
if (pos.non_pawn_material(Them) <= KnightValueMg)
ebonus += ebonus / 4;
else if (pos.pieces(ROOK, QUEEN, Them))
else if (pos.pieces(Them, ROOK, QUEEN))
ebonus -= ebonus / 4;
}
score += make_score(mbonus, ebonus);
@@ -887,29 +973,29 @@ namespace {
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(flip(c)))
if (pos.non_pawn_material(~c))
continue;
b = ei.pi->passed_pawns(c);
while (b)
{
s = pop_1st_bit(&b);
queeningSquare = relative_square(c, make_square(file_of(s), RANK_8));
queeningPath = squares_in_front_of(c, s);
s = pop_lsb(&b);
queeningSquare = relative_square(c, file_of(s) | RANK_8);
queeningPath = forward_bb(c, s);
// Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(flip(c)), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
// Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers.
assert(!pos.in_check());
assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces(c)));
assert(!pos.checkers());
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition
movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
@@ -928,24 +1014,24 @@ namespace {
return SCORE_ZERO;
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
loserSide = flip(winnerSide);
loserSide = ~winnerSide;
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
b = candidates = pos.pieces(PAWN, loserSide);
b = candidates = pos.pieces(loserSide, PAWN);
while (b)
{
s = pop_1st_bit(&b);
s = pop_lsb(&b);
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Check if (without even considering any obstacles) we're too far away or doubled
if ( pliesToQueen[winnerSide] + 3 <= pliesToGo
|| (squares_in_front_of(loserSide, s) & pos.pieces(PAWN, loserSide)))
clear_bit(&candidates, s);
|| (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN)))
candidates ^= s;
}
// If any candidate is already a passed pawn it _may_ promote in time. We give up.
@@ -957,26 +1043,26 @@ namespace {
while (b)
{
s = pop_1st_bit(&b);
s = pop_lsb(&b);
sacptg = blockersCount = 0;
minKingDist = kingptg = 256;
// Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Generate list of blocking pawns and supporters
supporters = neighboring_files_bb(file_of(s)) & candidates;
opposed = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide);
supporters = adjacent_files_bb(file_of(s)) & candidates;
opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN);
assert(blockers);
// How many plies does it take to remove all the blocking pawns?
while (blockers)
{
blockSq = pop_1st_bit(&blockers);
blockSq = pop_lsb(&blockers);
movesToGo = 256;
// Check pawns that can give support to overcome obstacle, for instance
@@ -987,7 +1073,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
movesToGo = std::min(movesToGo, d);
}
}
@@ -997,7 +1083,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
movesToGo = std::min(movesToGo, d);
}
@@ -1026,7 +1112,7 @@ namespace {
}
// Winning pawn is unstoppable and will promote as first, return big score
Score score = make_score(0, (Value) 0x500 - 0x20 * pliesToQueen[winnerSide]);
Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]);
return winnerSide == WHITE ? score : -score;
}
@@ -1046,31 +1132,27 @@ namespace {
// SpaceMask[]. A square is unsafe if it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safe = SpaceMask[Us]
& ~pos.pieces(PAWN, Us)
& ~pos.pieces(Us, PAWN)
& ~ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]);
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
// Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(PAWN, Us);
Bitboard behind = pos.pieces(Us, PAWN);
behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
return popcount<Max15>(safe) + popcount<Max15>(behind & safe);
// Since SpaceMask[Us] is fully on our half of the board
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
// Count safe + (behind & safe) with a single popcount
return popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
}
// apply_weight() applies an evaluation weight to a value trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
// scale_by_game_phase() interpolates between a middle game and an endgame score,
// interpolate() interpolates between a middle game and an endgame score,
// based on game phase. It also scales the return value by a ScaleFactor array.
Value scale_by_game_phase(const Score& v, Phase ph, ScaleFactor sf) {
Value interpolate(const Score& v, Phase ph, ScaleFactor sf) {
assert(mg_value(v) > -VALUE_INFINITE && mg_value(v) < VALUE_INFINITE);
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
@@ -1095,48 +1177,22 @@ namespace {
}
// init_safety() initizes the king safety evaluation, based on UCI
// parameters. It is called from read_weights().
void init_safety() {
const Value MaxSlope = Value(30);
const Value Peak = Value(1280);
Value t[100];
// First setup the base table
for (int i = 0; i < 100; i++)
{
t[i] = Value(int(0.4 * i * i));
if (i > 0)
t[i] = std::min(t[i], t[i - 1] + MaxSlope);
t[i] = std::min(t[i], Peak);
}
// Then apply the weights and get the final KingDangerTable[] array
for (Color c = WHITE; c <= BLACK; c++)
for (int i = 0; i < 100; i++)
KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
}
// A couple of little helpers used by tracing code, to_cp() converts a value to
// a double in centipawns scale, trace_add() stores white and black scores.
double to_cp(Value v) { return double(v) / double(PawnValueMidgame); }
double to_cp(Value v) { return double(v) / double(PawnValueMg); }
void trace_add(int idx, Score wScore, Score bScore) {
TracedScores[WHITE][idx] = wScore;
TracedScores[BLACK][idx] = bScore;
TracedScores[WHITE][idx] = wScore;
TracedScores[BLACK][idx] = bScore;
}
// trace_row() is an helper function used by tracing code to register the
// values of a single evaluation term.
void trace_row(const char *name, int idx) {
void trace_row(const char* name, int idx) {
Score wScore = TracedScores[WHITE][idx];
Score bScore = TracedScores[BLACK][idx];
@@ -1159,47 +1215,3 @@ namespace {
}
}
}
/// trace_evaluate() is like evaluate() but instead of a value returns a string
/// suitable to be print on stdout with the detailed descriptions and values of
/// each evaluation term. Used mainly for debugging.
std::string trace_evaluate(const Position& pos) {
Value margin;
std::string totals;
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
}
+7 -3
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,8 +24,12 @@
class Position;
namespace Eval {
extern void init();
extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace_evaluate(const Position& pos);
extern void read_evaluation_uci_options(Color sideToMove);
extern std::string trace(const Position& pos);
}
#endif // !defined(EVALUATE_H_INCLUDED)
-71
View File
@@ -1,71 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
#include "types.h"
#include <cstring>
#include <algorithm>
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
/// statistics are used for reduction and move ordering decisions. History
/// entries are stored according only to moving piece and destination square,
/// in particular two moves with different origin but same destination and
/// same piece will be considered identical.
class History {
public:
void clear();
Value value(Piece p, Square to) const;
void add(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
static const Value MaxValue = Value(2000);
private:
Value history[16][64]; // [piece][to_square]
Value maxGains[16][64]; // [piece][to_square]
};
inline void History::clear() {
memset(history, 0, 16 * 64 * sizeof(Value));
memset(maxGains, 0, 16 * 64 * sizeof(Value));
}
inline Value History::value(Piece p, Square to) const {
return history[p][to];
}
inline void History::add(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
}
inline Value History::gain(Piece p, Square to) const {
return maxGains[p][to];
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED)
-85
View File
@@ -1,85 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
#if !defined(_MSC_VER)
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL)
# define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x)
# define cond_destroy(x) pthread_cond_destroy(x)
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
#else
#define NOMINMAX // disable macros min() and max()
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0)
# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
# define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x)
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
#endif
#endif
#endif // !defined(LOCK_H_INCLUDED)
+16 -21
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,37 +21,32 @@
#include <string>
#include "bitboard.h"
#include "misc.h"
#include "evaluate.h"
#include "position.h"
#include "search.h"
#include "thread.h"
using namespace std;
extern void uci_loop();
extern void benchmark(int argc, char* argv[]);
extern void kpk_bitbase_init();
#include "tt.h"
#include "ucioption.h"
int main(int argc, char* argv[]) {
bitboards_init();
Position::init();
kpk_bitbase_init();
std::cout << engine_info() << std::endl;
UCI::init(Options);
Bitboards::init();
Zobrist::init();
Bitbases::init_kpk();
Search::init();
Eval::init();
Threads.init();
TT.set_size(Options["Hash"]);
cout << engine_info() << endl;
std::string args;
if (argc == 1)
uci_loop();
for (int i = 1; i < argc; i++)
args += std::string(argv[i]) + " ";
else if (string(argv[1]) == "bench")
benchmark(argc, argv);
else
cerr << "\nUsage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
UCI::loop(args);
Threads.exit();
}
+112 -106
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::min
#include <cassert>
#include <cstring>
#include <algorithm>
#include "material.h"
@@ -38,15 +38,29 @@ namespace {
const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
// pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][8] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
// pair pawn knight bishop rook queen
{ 7 }, // Bishop pair
{ 39, 2 }, // Pawn
{ 35, 271, -4 }, // Knight
{ 7, 105, 4, 7 }, // Bishop
{ -27, -2, 46, 100, 56 }, // Rook
{ 58, 29, 83, 148, -3, -25 } // Queen
};
const int QuadraticCoefficientsOppositeColor[][8] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
// THEIR PIECES
// pair pawn knight bishop rook queen
{ 41 }, // Bishop pair
{ 37, 41 }, // Pawn
{ 10, 62, 41 }, // Knight OUR PIECES
{ 57, 64, 39, 41 }, // Bishop
{ 50, 40, 23, -22, 41 }, // Rook
{ 106, 101, 3, 151, 171, 41 } // Queen
};
// Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key.
@@ -63,11 +77,11 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame;
&& pos.non_pawn_material(Us) >= RookValueMg;
}
template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame
return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
}
@@ -75,76 +89,102 @@ namespace {
template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0
&& pos.non_pawn_material(Us) == QueenValueMidgame
&& pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
/// imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
} // namespace
namespace Material {
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
/// Material::probe() takes a position object as input, looks up a MaterialEntry
/// object, and returns a pointer to it. If the material configuration is not
/// already present in the table, it is computed and stored there, so we don't
/// have to recompute everything when the same material configuration occurs again.
void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); }
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
/// MaterialInfoTable::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
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Key key = pos.material_key();
MaterialInfo* mi = probe(key);
Entry* e = entries[key];
// If mi->key matches the position's material hash key, it means that we
// If e->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it.
if (mi->key == key)
return mi;
if (e->key == key)
return e;
// Initialize MaterialInfo entry
memset(mi, 0, sizeof(MaterialInfo));
mi->key = key;
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = 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.
if ((mi->evaluationFunction = funcs->get<Value>(key)) != NULL)
return mi;
if (endgames.probe(key, e->evaluationFunction))
return e;
if (is_KXK<WHITE>(pos))
{
mi->evaluationFunction = &EvaluateKXK[WHITE];
return mi;
e->evaluationFunction = &EvaluateKXK[WHITE];
return e;
}
if (is_KXK<BLACK>(pos))
{
mi->evaluationFunction = &EvaluateKXK[BLACK];
return mi;
e->evaluationFunction = &EvaluateKXK[BLACK];
return e;
}
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{
// Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK.
assert((pos.pieces(KNIGHT, WHITE) | pos.pieces(BISHOP, WHITE)));
assert((pos.pieces(KNIGHT, BLACK) | pos.pieces(BISHOP, BLACK)));
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return mi;
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return e;
}
}
@@ -155,26 +195,26 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
// scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<ScaleFactor>(key)) != NULL)
if (endgames.probe(key, sf))
{
mi->scalingFunction[sf->color()] = sf;
return mi;
e->scalingFunction[sf->color()] = sf;
return e;
}
// Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
if (is_KBPsKs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
if (is_KQKRPs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
@@ -184,99 +224,63 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
if (pos.piece_count(BLACK, PAWN) == 0)
{
assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (pos.piece_count(WHITE, PAWN) == 0)
{
assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
}
}
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg)
{
mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg)
{
mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame)
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
e->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[2][8] = {
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(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) } };
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi;
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return e;
}
/// MaterialInfoTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
}
/// MaterialInfoTable::game_phase() calculates the phase given the current
/// Material::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialInfo.
/// is stored in MaterialEntry.
Phase MaterialInfoTable::game_phase(const Position& pos) {
Phase game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
@@ -284,3 +288,5 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
} // namespace Material
+22 -69
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,104 +21,57 @@
#define MATERIAL_H_INCLUDED
#include "endgame.h"
#include "misc.h"
#include "position.h"
#include "tt.h"
#include "types.h"
const int MaterialTableSize = 8192;
namespace Material {
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will
/// be used), and "scale factors" for black and white.
/// Material::Entry contains various information about a material configuration.
/// It contains a material balance evaluation, a function pointer to a special
/// endgame evaluation function (which in most cases is NULL, meaning that the
/// standard evaluation function will be used), and "scale factors".
///
/// The scale factors are used to scale the evaluation score up or down.
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn.
class MaterialInfo {
struct Entry {
friend class MaterialInfoTable;
public:
Score material_value() const;
Score material_value() const { return make_score(value, value); }
int space_weight() const { return spaceWeight; }
Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
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;
private:
Key key;
int16_t value;
uint8_t factor[2];
uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[2];
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
int spaceWeight;
Phase gamePhase;
};
typedef HashTable<Entry, 8192> Table;
/// The MaterialInfoTable class represents a pawn hash table. The most important
/// method is material_info(), which returns a pointer to a MaterialInfo object.
Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
Phase game_phase(const Position& pos);
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public:
~MaterialInfoTable();
void init();
MaterialInfo* material_info(const Position& pos) const;
static Phase game_phase(const Position& pos);
private:
template<Color Us>
static int imbalance(const int pieceCount[][8]);
Endgames* funcs;
};
/// MaterialInfo::scale_factor takes a position and a color as input, and
/// Material::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the
/// position in addition to the color, because the scale factor need not
/// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const {
inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
if (!scalingFunction[c])
return ScaleFactor(factor[c]);
ScaleFactor sf = (*scalingFunction[c])(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
}
inline Value MaterialInfo::evaluate(const Position& pos) const {
return (*evaluationFunction)(pos);
}
inline Score MaterialInfo::material_value() const {
return make_score(value, value);
}
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
inline Phase MaterialInfo::game_phase() const {
return gamePhase;
}
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
#endif // !defined(MATERIAL_H_INCLUDED)
+116 -73
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,52 +17,30 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE
#define NOMINMAX // disable macros min() and max()
#include <windows.h>
#include <sys/timeb.h>
#else
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
# if defined(__hpux)
# include <sys/pstat.h>
# endif
#endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iomanip>
#include <iostream>
#include <sstream>
#include "bitcount.h"
#include "misc.h"
#include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
using namespace std;
/// Version number. If Version is left empty, then Tag plus current
/// date (in the format YYMMDD) is used as a version number.
/// date, in the format DD-MM-YY, are used as a version number.
static const string Version = "2.2";
static const string Tag = "";
static const string Version = "3";
static const string Tag = "";
/// engine_info() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending
/// on whether Version is empty.
/// engine_info() returns the full name of the current Stockfish version. This
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
/// the program was compiled) or "Stockfish <Version>", depending on whether
/// Version is empty.
const string engine_info(bool to_uci) {
@@ -73,25 +51,30 @@ const string engine_info(bool to_uci) {
string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
s << "Stockfish " << Version;
if (Version.empty())
{
date >> month >> day >> year;
s << "Stockfish " << Tag
<< setfill('0') << " " << year.substr(2)
<< setw(2) << (1 + months.find(month) / 4)
<< setw(2) << day << cpu64 << popcnt;
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
}
else
s << "Stockfish " << Version << cpu64 << popcnt;
s << (to_uci ? "\nid author ": " by ")
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str();
}
/// Convert system time to milliseconds. That's all we need.
Time::point Time::now() {
sys_time_t t; system_time(&t); return time_to_msec(t);
}
/// Debug functions used mainly to collect run-time statistics
static uint64_t hits[2], means[2];
@@ -112,39 +95,102 @@ void dbg_print() {
}
/// system_time() returns the current system time, measured in milliseconds
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
/// can toggle the logging of std::cout and std:cin at runtime while preserving
/// usual i/o functionality and without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
int system_time() {
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
#if defined(_MSC_VER)
struct _timeb t;
_ftime(&t);
return int(t.time * 1000 + t.millitm);
#else
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000 + t.tv_usec / 1000;
#endif
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {}
int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); }
int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
int underflow() { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); }
streambuf* buf;
ofstream* file;
int log(int c, const char* prefix) {
static int last = '\n';
if (last == '\n')
file->rdbuf()->sputn(prefix, 3);
return last = file->rdbuf()->sputc((char)c);
}
};
class Logger {
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {}
~Logger() { start(false); }
ofstream file;
Tie in, out;
public:
static void start(bool b) {
static Logger l;
if (b && !l.file.is_open())
{
l.file.open("io_log.txt", ifstream::out | ifstream::app);
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
else if (!b && l.file.is_open())
{
cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf);
l.file.close();
}
}
};
/// Used to serialize access to std::cout to avoid multiple threads to write at
/// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == io_lock)
m.lock();
if (sc == io_unlock)
m.unlock();
return os;
}
/// Trampoline helper to avoid moving Logger to misc.h
void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_MSC_VER)
#if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s;
GetSystemInfo(&s);
return std::min(int(s.dwNumberOfProcessors), MAX_THREADS);
return s.dwNumberOfProcessors;
#else
# if defined(_SC_NPROCESSORS_ONLN)
return std::min((int)sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
return sysconf(_SC_NPROCESSORS_ONLN);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return std::min((int)psd.psd_proc_cnt, MAX_THREADS);
return psd.psd_proc_cnt;
# else
return 1;
# endif
@@ -156,24 +202,16 @@ int cpu_count() {
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition* sleepCond, Lock* sleepLock, int msec) {
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
#if defined(_MSC_VER)
#if defined(_WIN32) || defined(_WIN64)
int tm = msec;
#else
struct timeval t;
struct timespec abstime, *tm = &abstime;
timespec ts, *tm = &ts;
uint64_t ms = Time::now() + msec;
gettimeofday(&t, NULL);
abstime.tv_sec = t.tv_sec + (msec / 1000);
abstime.tv_nsec = (t.tv_usec + (msec % 1000) * 1000) * 1000;
if (abstime.tv_nsec > 1000000000LL)
{
abstime.tv_sec += 1;
abstime.tv_nsec -= 1000000000LL;
}
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000LL;
#endif
cond_timedwait(sleepCond, sleepLock, tm);
@@ -191,14 +229,19 @@ void prefetch(char*) {}
void prefetch(char* addr) {
# if defined(__INTEL_COMPILER) || defined(__ICL)
# if defined(__INTEL_COMPILER)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
# endif
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch(addr, _MM_HINT_T0);
_mm_prefetch(addr+64, _MM_HINT_T0); // 64 bytes ahead
# else
__builtin_prefetch(addr);
__builtin_prefetch(addr+64);
# endif
}
#endif
+27 -8
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,29 +22,48 @@
#include <fstream>
#include <string>
#include <vector>
#include "lock.h"
#include "types.h"
extern const std::string engine_info(bool to_uci = false);
extern int system_time();
extern int cpu_count();
extern void timed_wait(WaitCondition*, Lock*, int);
extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr);
extern void start_logger(bool b);
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_mean_of(int v);
extern void dbg_print();
class Position;
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_uci(Move m, bool chess960);
extern const std::string move_to_san(Position& pos, Move m);
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
namespace Time {
typedef int64_t point;
point now();
}
template<class Entry, int Size>
struct HashTable {
HashTable() : e(Size, Entry()) {}
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; }
private:
std::vector<Entry> e;
};
enum SyncCout { io_lock, io_unlock };
std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED)
-159
View File
@@ -1,159 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring>
#include <string>
#include "movegen.h"
#include "position.h"
using std::string;
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode. Instead internally Move is coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) {
Square from = move_from(m);
Square to = move_to(m);
string promotion;
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (is_castle(m) && !chess960)
to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
if (is_promotion(m))
promotion = char(tolower(piece_type_to_char(promotion_piece_type(m))));
return square_to_string(from) + square_to_string(to) + promotion;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
Move move_from_uci(const Position& pos, const string& str) {
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move for the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(is_ok(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
Square sq, from = move_from(m);
Square to = move_to(m);
PieceType pt = type_of(pos.piece_on(from));
string san;
if (is_castle(m))
san = (move_to(m) < move_from(m) ? "O-O-O" : "O-O");
else
{
if (pt != PAWN)
{
san = piece_type_to_char(pt);
// Disambiguation if we have more then one piece with destination 'to'
// note that for pawns is not needed because starting file is explicit.
attackers = pos.attackers_to(to) & pos.pieces(pt, pos.side_to_move());
clear_bit(&attackers, from);
ambiguousMove = ambiguousFile = ambiguousRank = false;
while (attackers)
{
sq = pop_1st_bit(&attackers);
// Pinned pieces are not included in the possible sub-set
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
if (file_of(sq) == file_of(from))
ambiguousFile = true;
if (rank_of(sq) == rank_of(from))
ambiguousRank = true;
ambiguousMove = true;
}
if (ambiguousMove)
{
if (!ambiguousFile)
san += file_to_char(file_of(from));
else if (!ambiguousRank)
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
}
if (pos.is_capture(m))
{
if (pt == PAWN)
san += file_to_char(file_of(from));
san += 'x';
}
san += square_to_string(to);
if (is_promotion(m))
{
san += '=';
san += piece_type_to_char(promotion_piece_type(m));
}
}
// The move gives check? We don't use pos.move_gives_check() here
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
return san;
}
+302 -410
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,268 +18,380 @@
*/
#include <cassert>
#include <algorithm>
#include "bitcount.h"
#include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'.
#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b))
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'.
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b))
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(*mlist++).move = make_move(to - (d), to); }
namespace {
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE
};
template<CastlingSide Side, bool Checks, bool Chess960>
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
template<CastlingSide>
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us);
template<Color, MoveType>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN && Pt != PAWN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING)
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b;
Square from;
const Square* pl = pos.piece_list(us, Pt);
if ((from = *pl++) == SQ_NONE)
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(us, Side);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us);
do
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
assert(!pos.checkers());
if (dc && bit_is_set(dc, from))
continue;
const int K = Chess960 ? kto > kfrom ? -1 : 1
: Side == KING_SIDE ? -1 : 1;
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
for (Square s = kto; s != kfrom; s += (Square)K)
if (pos.attackers_to(s) & enemies)
return mlist;
} while ((from = *pl++) != SQ_NONE);
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
return mlist;
(*mlist++).move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--;
return mlist;
}
template<>
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq)
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq));
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8
: Delta == DELTA_S ? p >> 8
: Delta == DELTA_NE ? (p & ~FileHBB) << 9
: Delta == DELTA_SE ? (p & ~FileHBB) >> 7
: Delta == DELTA_NW ? (p & ~FileABB) << 7
: Delta == DELTA_SW ? (p & ~FileABB) >> 9 : 0;
}
template<PieceType Pt, MoveType Type>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN);
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION);
template<GenType Type, Square Delta>
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) {
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
while (b)
{
Square to = pop_lsb(&b);
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK);
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP);
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
}
// Knight-promotion is the only one that can give a direct check not
// already included in the queen-promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
}
return mlist;
}
template<PieceType Pt>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from;
template<Color Us, GenType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist,
Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares;
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
Type == CAPTURES ? target : pos.pieces(Them));
// Single and double pawn pushes, no promotions
if (Type != CAPTURES)
{
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
{
b1 &= target;
b2 &= target;
}
if (Type == QUIET_CHECKS)
{
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
// Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovery check
// promotion has been already generated among captures.
if (pawnsNotOn7 & ci->dcCandidates)
{
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_PAWNS(b1, UP);
SERIALIZE_PAWNS(b2, UP + UP);
}
// Promotions and underpromotions
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
{
if (Type == CAPTURES)
emptySquares = ~pos.pieces();
if (Type == EVASIONS)
emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci);
}
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies;
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, RIGHT);
SERIALIZE_PAWNS(b2, LEFT);
if (pos.ep_square() != SQ_NONE)
{
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - UP)))
return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
}
}
return mlist;
}
template<PieceType Pt, bool Checks> FORCE_INLINE
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.piece_list(us, Pt);
if (*pl != SQ_NONE)
for (Square from = *pl; from != SQ_NONE; from = *++pl)
{
do {
from = *pl;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++pl != SQ_NONE);
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue;
if (ci->dcCandidates && (ci->dcCandidates & from))
continue;
}
Bitboard b = pos.attacks_from<Pt>(from) & target;
if (Checks)
b &= ci->checkSq[Pt];
SERIALIZE(b);
}
return mlist;
}
template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from = pos.king_square(us);
template<GenType Type> FORCE_INLINE
MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS;
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b);
}
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us))
{
if (pos.is_chess960())
{
mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us);
}
else
{
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us);
}
}
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
}
}
} // namespace
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
/// generate<CAPTURES> generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_CAPTURE> generates all pseudo-legal non-captures and
/// generate<QUIETS> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
template<MoveType Type>
template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(!pos.in_check());
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.checkers());
Color us = pos.side_to_move();
Bitboard target;
if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
target = pos.pieces(flip(us));
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else
assert(false);
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
: Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
if (Type == MV_NON_EVASION)
{
mlist = generate_piece_moves<PAWN, MV_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, MV_NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
target |= pos.empty_squares();
}
else
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
if (Type != MV_CAPTURE && pos.can_castle(us))
{
if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
}
return mlist;
return generate_all<Type>(pos, mlist, us, target);
}
// Explicit template instantiations
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*);
template MoveStack* generate<QUIETS>(const Position&, MoveStack*);
template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
/// generate<MV_NON_CAPTURE_CHECK> generates all pseudo-legal non-captures and knight
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
assert(!pos.in_check());
assert(!pos.checkers());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move();
Square ksq = pos.king_square(flip(us));
CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
assert(pos.piece_on(ksq) == make_piece(flip(us), KING));
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates();
while (b)
while (dc)
{
from = pop_1st_bit(&b);
switch (type_of(pos.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;
}
Square from = pop_lsb(&dc);
PieceType pt = type_of(pos.piece_on(from));
if (pt == PAWN)
continue; // Will be generated togheter with direct checks
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
if (pt == KING)
b &= ~PseudoAttacks[QUEEN][ci.ksq];
SERIALIZE(b);
}
// 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);
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci);
}
/// generate<MV_EVASION> generates all pseudo-legal check evasions when the side
/// 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.
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
assert(pos.in_check());
assert(pos.checkers());
Bitboard b, target;
Square from, checksq;
int checkersCnt = 0;
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = 0;
Bitboard b = pos.checkers();
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
assert(pos.checkers());
// Find squares attacked by slider checkers, we will remove
// them from the king evasions set so to early skip known
// illegal moves and avoid an useless legality check later.
b = checkers;
// Find squares attacked by slider checkers, we will remove them from the king
// evasions so to skip known illegal moves avoiding useless legality check later.
do
{
checkersCnt++;
checksq = pop_1st_bit(&b);
checksq = pop_lsb(&b);
assert(color_of(pos.piece_on(checksq)) == flip(us));
assert(color_of(pos.piece_on(checksq)) == ~us);
switch (type_of(pos.piece_on(checksq)))
{
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
case QUEEN:
// If queen and king are far we can safely remove all the squares attacked
// in the other direction becuase are not reachable by the king anyway.
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq)))
sliderAttacks |= QueenPseudoAttacks[checksq];
// If queen and king are far or not on a diagonal line we can safely
// remove all the squares attacked in the other direction becuase are
// not reachable by the king anyway.
if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq))
sliderAttacks |= PseudoAttacks[QUEEN][checksq];
// Otherwise, if king and queen are adjacent and on a diagonal line, we need to
// use real rook attacks to check if king is safe to move in the other direction.
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1.
// Otherwise we need to use real rook attacks to check if king is safe
// to move in the other direction. For example: king in B2, queen in A1
// a knight in B1, and we can safely move to C1.
else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
@@ -289,255 +401,35 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq;
SERIALIZE_MOVES(b);
SERIALIZE(b);
// Generate evasions for other pieces only if not double check
if (checkersCnt > 1)
return mlist;
return mlist; // Double check, only a king move can save the day
// Find squares where a blocking evasion or a capture of the
// checker piece is possible.
target = squares_between(checksq, ksq) | checkers;
// Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
mlist = generate_piece_moves<PAWN, MV_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);
return generate_all<EVASIONS>(pos, mlist, us, target);
}
/// generate<MV_LEGAL> computes a complete list of legal moves in the current position
/// generate<LEGAL> generates all the legal moves in the given position
template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
MoveStack *last, *cur = mlist;
MoveStack *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move());
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
// Remove illegal moves from the list
while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
: generate<NON_EVASIONS>(pos, mlist);
while (cur != end)
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
&& !pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--end)->move;
else
cur++;
return last;
return end;
}
namespace {
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b = move_pawns<Delta>(pawns) & target & ~TFileABB;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Promotions and under-promotions, both captures and non-captures
b = move_pawns<Delta>(pawnsOn7) & target;
if (Delta != DELTA_N && Delta != DELTA_S)
b &= ~TFileABB;
while (b)
{
to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION)
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION)
{
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Delta > 0 ? BLACK : WHITE)))
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
}
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time, named
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them));
// Pre-calculate pawn pushes before changing emptySquares definition
if (Type != MV_CAPTURE)
{
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<UP>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == MV_EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == MV_CAPTURE)
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB;
mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
}
// Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION)
{
mlist = generate_pawn_captures<Type, RIGHT_UP>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, LEFT_UP>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
// Consider only pawn moves which give direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard
{
dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -UP);
SERIALIZE_MOVES_D(b2, -UP -UP);
}
// En passant captures
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6);
assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3);
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_enpassant_move(to, pos.ep_square());
}
}
return mlist;
}
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us);
Color them = flip(us);
// After castling, the rook and king's final positions are exactly the same
// in Chess960 as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(f);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
// Unimpeded rule: All the squares between the king's initial and final squares
// (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & pos.pieces(them)))
return mlist;
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (pos.is_chess960())
{
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, rfrom);
if (pos.attackers_to(kto, occ) & pos.pieces(them))
return mlist;
}
(*mlist++).move = make_castle_move(kfrom, rfrom);
return mlist;
}
} // namespace
+15 -12
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,31 +22,34 @@
#include "types.h"
enum MoveType {
MV_CAPTURE,
MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL
enum GenType {
CAPTURES,
QUIETS,
QUIET_CHECKS,
EVASIONS,
NON_EVASIONS,
LEGAL
};
class Position;
template<MoveType>
template<GenType>
MoveStack* generate(const Position& pos, MoveStack* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
/// handy to use this class instead of the low level generate() function.
template<MoveType T>
template<GenType T>
struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
void operator++() { cur++; }
bool end() const { return cur == last; }
Move move() const { return cur->move; }
int size() const { return int(last - mlist); }
size_t size() const { return last - mlist; }
bool contains(Move m) const {
for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true;
return false;
}
private:
MoveStack mlist[MAX_MOVES];
+211 -233
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,223 +18,145 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "movegen.h"
#include "movepick.h"
#include "search.h"
#include "types.h"
#include "thread.h"
namespace {
enum MovegenPhase {
PH_TT_MOVE, // Transposition table move
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= captureThreshold (captureThreshold <= 0)
PH_GOOD_PROBCUT, // Queen promotions and captures with SEE values > captureThreshold (captureThreshold >= 0)
PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES_1, // Non-captures and underpromotions with positive score
PH_NONCAPTURES_2, // Non-captures and underpromotions with non-positive score
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < captureThreshold (captureThreshold <= 0)
PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search
PH_QRECAPTURES, // Recaptures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
enum Sequencer {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
EVASION, EVASIONS_S2,
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
QSEARCH_1, CAPTURES_S4,
PROBCUT, CAPTURES_S5,
RECAPTURE, CAPTURES_S6,
STOP
};
CACHE_LINE_ALIGNMENT
const uint8_t MainSearchTable[] = { PH_TT_MOVE, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES_1, PH_NONCAPTURES_2, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVE, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_STOP };
const uint8_t QsearchRecapturesTable[] = { PH_TT_MOVE, PH_QRECAPTURES, PH_STOP };
const uint8_t ProbCutTable[] = { PH_TT_MOVE, PH_GOOD_PROBCUT, PH_STOP };
// Our insertion sort, guaranteed to be stable, as is needed
void insertion_sort(MoveStack* begin, MoveStack* end)
{
MoveStack tmp, *p, *q;
for (p = begin + 1; p < end; ++p)
{
tmp = *p;
for (q = p; q != begin && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
// Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& move) { return move.score > 0; }
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
// Picks and pushes to the front the best move in range [firstMove, lastMove),
// Picks and moves to the front the best move in the range [begin, end),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end)
{
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
return firstMove;
std::swap(*begin, *std::max_element(begin, end));
return begin;
}
}
/// Constructors for the MovePicker class. As arguments we pass information
/// Constructors of the MovePicker class. As arguments we pass information
/// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) {
captureThreshold = 0;
badCaptures = moves + MAX_MOVES;
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) {
assert(d > DEPTH_ZERO);
if (p.in_check())
{
killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
captureThreshold = 0;
cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1;
ss = s;
if (p.checkers())
phase = EVASION;
else
{
phase = MAIN_SEARCH;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMidgame;
if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMg;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->eval > beta)
captureThreshold = beta - ss->eval;
phasePtr = MainSearchTable;
else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->staticEval;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
end += (ttMove != MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, Square recaptureSq)
: pos(p), H(h) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Square sq) : pos(p), Hist(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO);
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable;
else if (d >= DEPTH_QS_RECAPTURES)
{
phasePtr = QsearchWithoutChecksTable;
if (p.checkers())
phase = EVASION;
// Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.is_capture_or_promotion(ttm))
else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES)
{
phase = QSEARCH_1;
// Skip TT move if is not a capture or a promotion, this avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases
// when TT table is full.
if (ttm && !pos.is_capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else
{
phasePtr = QsearchRecapturesTable;
recaptureSquare = recaptureSq;
phase = RECAPTURE;
recaptureSquare = sq;
ttm = MOVE_NONE;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
end += (ttMove != MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType parentCapture)
: pos(p), H(h) {
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), Hist(h), cur(moves), end(moves) {
assert (!pos.in_check());
assert(!pos.checkers());
// In ProbCut we consider only captures better than parent's move
captureThreshold = PieceValueMidgame[Piece(parentCapture)];
phasePtr = ProbCutTable;
if ( ttm != MOVE_NONE
&& (!pos.is_capture(ttm) || pos.see(ttm) <= captureThreshold))
ttm = MOVE_NONE;
phase = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[MG][pt];
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
ttMove = MOVE_NONE;
end += (ttMove != MOVE_NONE);
}
/// MovePicker::go_next_phase() generates, scores and sorts the next bunch
/// of moves when there are no more moves to try for the current phase.
void MovePicker::go_next_phase() {
curMove = moves;
phase = *(++phasePtr);
switch (phase) {
case PH_TT_MOVE:
lastMove = curMove + 1;
return;
case PH_GOOD_CAPTURES:
case PH_GOOD_PROBCUT:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_KILLERS:
curMove = killers;
lastMove = curMove + 2;
return;
case PH_NONCAPTURES_1:
lastNonCapture = lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures();
lastMove = std::partition(curMove, lastMove, has_positive_score);
sort<MoveStack>(curMove, lastMove);
return;
case PH_NONCAPTURES_2:
curMove = lastMove;
lastMove = lastNonCapture;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(curMove, lastMove);
return;
case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just pick
// them in order to get SEE move ordering.
curMove = badCaptures;
lastMove = moves + MAX_MOVES;
return;
case PH_EVASIONS:
assert(pos.in_check());
lastMove = generate<MV_EVASION>(pos, moves);
score_evasions();
return;
case PH_QCAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
case PH_QRECAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
return;
case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
case PH_STOP:
lastMove = curMove + 1; // Avoid another go_next_phase() call
return;
default:
assert(false);
return;
}
}
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be
/// picked first by next_move().
void MovePicker::score_captures() {
/// score() assign a numerical move ordering score to each move in a move list.
/// The moves with highest scores will be picked first.
template<>
void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV/LVA.
// Suprisingly, this appears to perform slightly better than SEE based
// move ordering. The reason is probably that in a position with a winning
@@ -250,150 +172,206 @@ void MovePicker::score_captures() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m;
// Use MVV/LVA ordering
for (MoveStack* cur = moves; cur != lastMove; cur++)
for (MoveStack* it = moves; it != end; ++it)
{
m = cur->move;
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
- type_of(pos.piece_on(move_from(m)));
m = it->move;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m));
if (is_promotion(m))
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
if (type_of(m) == PROMOTION)
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
else if (type_of(m) == ENPASSANT)
it->score += PieceValue[MG][PAWN];
}
}
void MovePicker::score_noncaptures() {
template<>
void MovePicker::score<QUIETS>() {
Move m;
Square from;
for (MoveStack* cur = moves; cur != lastMove; cur++)
for (MoveStack* it = moves; it != end; ++it)
{
m = cur->move;
from = move_from(m);
cur->score = H.value(pos.piece_on(from), move_to(m));
m = it->move;
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
}
}
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.
template<>
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, then bad-captures and quiet
// moves with a negative SEE. This last group is ordered by the SEE score.
Move m;
int seeScore;
// Skip if we don't have at least two moves to order
if (lastMove < moves + 2)
return;
for (MoveStack* cur = moves; cur != lastMove; cur++)
for (MoveStack* it = moves; it != end; ++it)
{
m = cur->move;
m = it->move;
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
it->score = seeScore - History::Max; // At the bottom
else if (pos.is_capture(m))
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
- type_of(pos.piece_on(move_from(m))) + History::MaxValue;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::Max;
else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
}
}
/// MovePicker::next_move() is the most important method of the MovePicker class.
/// It returns a new pseudo legal move every time it is called, until there
/// are no more moves left. It picks the move with the biggest score from a list
/// of generated moves taking care not to return the tt move if has already been
/// searched previously. Note that this function is not thread safe so should be
/// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::next_move() {
/// generate_next() generates, scores and sorts the next bunch of moves, when
/// there are no more moves to try for the current phase.
void MovePicker::generate_next() {
cur = moves;
switch (++phase) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves);
score<CAPTURES>();
return;
case KILLERS_S1:
cur = killers;
end = cur + 2;
return;
case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves);
score<QUIETS>();
end = std::partition(cur, end, has_positive_score);
insertion_sort(cur, end);
return;
case QUIETS_2_S1:
cur = end;
end = endQuiets;
if (depth >= 3 * ONE_PLY)
insertion_sort(cur, end);
return;
case BAD_CAPTURES_S1:
// Just pick them in reverse order to get MVV/LVA ordering
cur = moves + MAX_MOVES - 1;
end = endBadCaptures;
return;
case EVASIONS_S2:
end = generate<EVASIONS>(pos, moves);
if (end > moves + 1)
score<EVASIONS>();
return;
case QUIET_CHECKS_S3:
end = generate<QUIET_CHECKS>(pos, moves);
return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP;
case STOP:
end = cur + 1; // Avoid another next_phase() call
return;
default:
assert(false);
}
}
/// next_move() is the most important method of the MovePicker class. It returns
/// a new pseudo legal move every time is called, until there are no more moves
/// left. It picks the move with the biggest score from a list of generated moves
/// taking care not returning the ttMove if has already been searched previously.
template<>
Move MovePicker::next_move<false>() {
Move move;
while (true)
{
while (curMove == lastMove)
go_next_phase();
while (cur == end)
generate_next();
switch (phase) {
case PH_TT_MOVE:
curMove++;
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
cur++;
return ttMove;
break;
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove)->move;
case CAPTURES_S1:
move = pick_best(cur++, end)->move;
if (move != ttMove)
{
assert(captureThreshold <= 0); // Otherwise we must use see instead of see_sign
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= captureThreshold)
if (pos.see_sign(move) >= captureThreshold)
return move;
// Losing capture, move it to the tail of the array
(--badCaptures)->move = move;
badCaptures->score = seeValue;
(endBadCaptures--)->move = move;
}
break;
case PH_GOOD_PROBCUT:
move = pick_best(curMove++, lastMove)->move;
if ( move != ttMove
&& pos.see(move) > captureThreshold)
return move;
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move)
&& move != ttMove
case KILLERS_S1:
move = (cur++)->move;
if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move)
&& move != ttMove
&& !pos.is_capture(move))
return move;
break;
case PH_NONCAPTURES_1:
case PH_NONCAPTURES_2:
move = (curMove++)->move;
case QUIETS_1_S1: case QUIETS_2_S1:
move = (cur++)->move;
if ( move != ttMove
&& move != killers[0].move
&& move != killers[1].move)
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove)->move;
return move;
case BAD_CAPTURES_S1:
return (cur--)->move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove)->move;
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
move = pick_best(cur++, end)->move;
if (move != ttMove)
return move;
break;
case PH_QRECAPTURES:
move = (curMove++)->move;
if (move_to(move) == recaptureSquare)
case CAPTURES_S5:
move = pick_best(cur++, end)->move;
if (move != ttMove && pos.see(move) > captureThreshold)
return move;
break;
case CAPTURES_S6:
move = pick_best(cur++, end)->move;
if (to_sq(move) == recaptureSquare)
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
case QUIET_CHECKS_S3:
move = (cur++)->move;
if (move != ttMove)
return move;
break;
case PH_STOP:
case STOP:
return MOVE_NONE;
default:
assert(false);
break;
}
}
}
/// Version of next_move() to use at split point nodes where the move is grabbed
/// from the split point's shared MovePicker object. This function is not thread
/// safe so must be lock protected by the caller.
template<>
Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }
+53 -19
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,18 +20,54 @@
#if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
#include "history.h"
#include <algorithm> // For std::max
#include <cstring> // For memset
#include "movegen.h"
#include "position.h"
#include "search.h"
#include "types.h"
/// MovePicker is a class which is used to pick one pseudo legal move at a time
/// from the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::next_move(), which returns a new pseudo legal move each time
/// it is called, until there are no moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to get a cut-off first.
/// The Stats struct stores moves statistics. According to the template parameter
/// the class can store both History and Gains type statistics. History records
/// how often different moves have been successful or unsuccessful during the
/// current search and is used for reduction and move ordering decisions. Gains
/// records the move's best evaluation gain from one ply to the next and is used
/// for pruning decisions. Entries are stored according only to moving piece and
/// destination square, in particular two moves with different origin but same
/// destination and same piece will be considered identical.
template<bool Gain>
struct Stats {
static const Value Max = Value(2000);
const Value* operator[](Piece p) const { return &table[p][0]; }
void clear() { memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Value v) {
if (Gain)
table[p][to] = std::max(v, table[p][to] - 1);
else if (abs(table[p][to] + v) < Max)
table[p][to] += v;
}
private:
Value table[PIECE_NB][SQUARE_NB];
};
typedef Stats<false> History;
typedef Stats<true> Gains;
/// MovePicker class is used to pick one pseudo legal move at a time from the
/// current position. The most important method is next_move(), which returns a
/// new pseudo legal move each time it is called, until there are no moves left,
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first.
class MovePicker {
@@ -39,25 +75,23 @@ class MovePicker {
public:
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&, Square recaptureSq);
MovePicker(const Position&, Move, const History&, PieceType parentCapture);
Move next_move();
MovePicker(const Position&, Move, Depth, const History&, Square);
MovePicker(const Position&, Move, const History&, PieceType);
template<bool SpNode> Move next_move();
private:
void score_captures();
void score_noncaptures();
void score_evasions();
void go_next_phase();
template<GenType> void score();
void generate_next();
const Position& pos;
const History& H;
const History& Hist;
Search::Stack* ss;
Depth depth;
Move ttMove;
MoveStack killers[2];
Square recaptureSquare;
int captureThreshold, phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastNonCapture, *badCaptures;
MoveStack *cur, *end, *endQuiets, *endBadCaptures;
MoveStack moves[MAX_MOVES];
};
+263
View File
@@ -0,0 +1,263 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <iomanip>
#include <sstream>
#include <stack>
#include "movegen.h"
#include "notation.h"
#include "position.h"
using namespace std;
static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
/// score_to_uci() converts a value to a string suitable for use with the UCI
/// protocol specifications:
///
/// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
/// use negative values for y.
string score_to_uci(Value v, Value alpha, Value beta) {
stringstream s;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
s << "cp " << v * 100 / int(PawnValueMg);
else
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
return s.str();
}
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
/// mode. Internally castle moves are always coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (type_of(m) == CASTLE && !chess960)
to = (to > from ? FILE_G : FILE_C) | rank_of(from);
string move = square_to_string(from) + square_to_string(to);
if (type_of(m) == PROMOTION)
move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
return move;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent legal Move if any.
Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// move_to_san() takes a position and a legal Move as input and returns its
/// short algebraic notation representation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(MoveList<LEGAL>(pos).contains(m));
Bitboard others, b;
string san;
Color us = pos.side_to_move();
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = pos.piece_on(from);
PieceType pt = type_of(pc);
if (type_of(m) == CASTLE)
san = to > from ? "O-O" : "O-O-O";
else
{
if (pt != PAWN)
{
san = PieceToChar[WHITE][pt]; // Upper case
// Disambiguation if we have more then one piece of type 'pt' that can
// reach 'to' with a legal move.
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (b)
{
Move move = make_move(pop_lsb(&b), to);
if (!pos.pl_move_is_legal(move, pos.pinned_pieces()))
others ^= from_sq(move);
}
if (others)
{
if (!(others & file_bb(from)))
san += file_to_char(file_of(from));
else if (!(others & rank_bb(from)))
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
}
else if (pos.is_capture(m))
san = file_to_char(file_of(from));
if (pos.is_capture(m))
san += 'x';
san += square_to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
}
if (pos.move_gives_check(m, CheckInfo(pos)))
{
StateInfo st;
pos.do_move(m, st);
san += MoveList<LEGAL>(pos).size() ? "+" : "#";
pos.undo_move(m);
}
return san;
}
/// pretty_pv() formats human-readable search information, typically to be
/// appended to the search log file. It uses the two helpers below to pretty
/// format time and score respectively.
static string time_to_string(int64_t msecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int64_t hours = msecs / MSecHour;
int64_t minutes = (msecs % MSecHour) / MSecMinute;
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
stringstream s;
if (hours)
s << hours << ':';
s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return s.str();
}
static string score_to_string(Value v) {
stringstream s;
if (v >= VALUE_MATE_IN_MAX_PLY)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
return s.str();
}
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
std::stack<StateInfo> st;
Move* m = pv;
string san, padding;
size_t length;
stringstream s;
s << setw(2) << depth
<< setw(8) << score_to_string(value)
<< setw(8) << time_to_string(msecs);
if (pos.nodes_searched() < M)
s << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << setw(7) << pos.nodes_searched() / K << "K ";
else
s << setw(7) << pos.nodes_searched() / M << "M ";
padding = string(s.str().length(), ' ');
length = padding.length();
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
if (length + san.length() > 80)
{
s << "\n" + padding;
length = padding.length();
}
s << san << ' ';
length += san.length() + 1;
st.push(StateInfo());
pos.do_move(*m++, st.top());
}
while (m != pv)
pos.undo_move(*--m);
return s.str();
}
+35
View File
@@ -0,0 +1,35 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(NOTATION_H_INCLUDED)
#define NOTATION_H_INCLUDED
#include <string>
#include "types.h"
class Position;
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
Move move_from_uci(const Position& pos, std::string& str);
const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED)
+196 -144
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,215 +26,267 @@
namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][8] = {
const Score DoubledPawnPenalty[2][FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][8] = {
const Score IsolatedPawnPenalty[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][8] = {
const Score BackwardPawnPenalty[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file
const Score ChainBonus[8] = {
const Score ChainBonus[FILE_NB] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
};
// Candidate passed pawn bonus by rank
const Score CandidateBonus[8] = {
const Score CandidateBonus[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0)
};
const Score PawnStructureWeight = S(233, 201);
#undef S
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
const Value ShelterWeakness[2][RANK_NB] =
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) },
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } };
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
const Value StormDanger[2][RANK_NB] =
{ { V(26), V(0), V(128), V(51), V(26) },
{ V(13), V(0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont.
const Value MaxSafetyBonus = V(263);
#undef S
#undef V
template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Square s;
File f;
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const Square* pl = pos.piece_list(Us, PAWN);
// Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = file_of(s);
r = rank_of(s);
// This file cannot be half open
e->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
// Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one).
chain = ourPawns & adjacent_files_bb(f) & b;
isolated = !(ourPawns & adjacent_files_bb(f));
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s));
// Test for backward pawn
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{
// We now know that there are no friendly pawns beside or behind this
// pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the adjacent
// files, and seeing whether we meet a friendly or an enemy pawn first.
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;
}
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher or equal than the number of
// enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
e->passedPawns[Us] |= s;
// Score this pawn
if (isolated)
value -= IsolatedPawnPenalty[opposed][f];
if (doubled)
value -= DoubledPawnPenalty[opposed][f];
if (backward)
value -= BackwardPawnPenalty[opposed][f];
if (chain)
value += ChainBonus[f];
if (candidate)
value += CandidateBonus[relative_rank(Us, s)];
}
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & BlackSquares);
e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK];
e->pawnsOnSquares[Them][BLACK] = popcount<Max15>(theirPawns & BlackSquares);
e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK];
return value;
}
}
namespace Pawns {
/// PawnInfoTable::pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
/// probe() takes a position object as input, computes a Entry object, and returns
/// a pointer to it. The result is also stored in a hash table, so we don't have
/// to recompute everything when the same pawn structure occurs again.
PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key();
PawnInfo* pi = probe(key);
Entry* e = entries[key];
// If pi->key matches the position's pawn hash key, it means that we
// If e->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it.
if (pi->key == key)
return pi;
if (e->key == key)
return e;
// Initialize PawnInfo entry
pi->key = key;
pi->passedPawns[WHITE] = pi->passedPawns[BLACK] = 0;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
e->key = key;
e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0;
e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE;
e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
// Calculate pawn attacks
Bitboard wPawns = pos.pieces(PAWN, WHITE);
Bitboard bPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB);
Bitboard wPawns = pos.pieces(WHITE, PAWN);
Bitboard bPawns = pos.pieces(BLACK, PAWN);
e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7);
e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9);
// Evaluate pawns for both colors and weight the result
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, e);
pi->value = apply_weight(pi->value, PawnStructureWeight);
e->value = apply_weight(e->value, PawnStructureWeight);
return pi;
return e;
}
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
/// Entry::shelter_storm() calculates shelter and storm penalties for the file
/// the king is on, as well as the two adjacent files.
template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) {
Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Square s;
File f;
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO;
const Square* pl = pos.piece_list(Us, PAWN);
Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = file_of(ksq);
// Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE)
kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;
for (int f = kf - 1; f <= kf + 1; f++)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
// Shelter penalty is higher for the pawn in front of the king
b = ourPawns & FileBB[f];
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= ShelterWeakness[f != kf][rkUs];
f = file_of(s);
r = rank_of(s);
// This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
// Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one).
passed = !(theirPawns & passed_pawn_mask(Us, s));
doubled = ourPawns & squares_in_front_of(Us, s);
opposed = theirPawns & squares_in_front_of(Us, s);
isolated = !(ourPawns & neighboring_files_bb(f));
chain = ourPawns & neighboring_files_bb(f) & b;
// Test for backward pawn
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on neighboring files
// or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{
// 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.
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;
}
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on neighboring files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s);
// Score this pawn
if (isolated)
value -= IsolatedPawnPenalty[opposed][f];
if (doubled)
value -= DoubledPawnPenalty[opposed][f];
if (backward)
value -= BackwardPawnPenalty[opposed][f];
if (chain)
value += ChainBonus[f];
if (candidate)
value += CandidateBonus[relative_rank(Us, s)];
// Storm danger is smaller if enemy pawn is blocked
b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
}
return value;
return safety;
}
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total king_shelter() calls.
/// Entry::update_safety() calculates and caches a bonus for king safety. It is
/// called only when king square changes, about 20% of total king_safety() calls.
template<Color Us>
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
Score Entry::update_safety(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8);
Bitboard pawns;
int r, shelter = 0;
if (relative_rank(Us, ksq) <= RANK_4)
{
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(file_of(ksq));
r = ksq & (7 << 3);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] << (6 - i);
}
}
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
castleRights[Us] = pos.can_castle(Us);
minKPdistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castle if is bigger
if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]);
}
// Explicit template instantiation
template Score PawnInfo::updateShelter<WHITE>(const Position& pos, Square ksq);
template Score PawnInfo::updateShelter<BLACK>(const Position& pos, Square ksq);
template Score Entry::update_safety<WHITE>(const Position& pos, Square ksq);
template Score Entry::update_safety<BLACK>(const Position& pos, Square ksq);
} // namespace Pawns
+35 -66
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,88 +20,57 @@
#if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED
#include "misc.h"
#include "position.h"
#include "tt.h"
#include "types.h"
const int PawnTableSize = 16384;
namespace Pawns {
/// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash
/// table (performed by calling the pawn_info method in a PawnInfoTable
/// object) returns a pointer to a PawnInfo object.
/// Pawns::Entry contains various information about a pawn structure. Currently,
/// it only includes a middle game and end game pawn structure evaluation, and a
/// bitboard of passed pawns. We may want to add further information in the future.
/// A lookup to the pawn hash table (performed by calling the probe function)
/// returns a pointer to an Entry object.
class PawnInfo {
struct Entry {
friend class PawnInfoTable;
public:
Score pawns_value() const;
Bitboard pawn_attacks(Color c) const;
Bitboard passed_pawns(Color c) const;
int file_is_half_open(Color c, File f) const;
int has_open_file_to_left(Color c, File f) const;
int has_open_file_to_right(Color c, File f) const;
Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); }
int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); }
int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); }
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
template<Color Us>
Score king_shelter(const Position& pos, Square ksq);
Score king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<Us>(pos, ksq);
}
private:
template<Color Us>
Score updateShelter(const Position& pos, Square ksq);
Score update_safety(const Position& pos, Square ksq);
template<Color Us>
Value shelter_storm(const Position& pos, Square ksq);
Key key;
Bitboard passedPawns[2];
Bitboard pawnAttacks[2];
Square kingSquares[2];
Bitboard passedPawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB];
int minKPdistance[COLOR_NB];
int castleRights[COLOR_NB];
Score value;
int halfOpenFiles[2];
Score kingShelters[2];
int halfOpenFiles[COLOR_NB];
Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB];
};
typedef HashTable<Entry, 16384> Table;
/// The PawnInfoTable class represents a pawn hash table. The most important
/// method is pawn_info, which returns a pointer to a PawnInfo object.
Entry* probe(const Position& pos, Table& entries);
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public:
PawnInfo* pawn_info(const Position& pos) const;
private:
template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
};
inline Score PawnInfo::pawns_value() const {
return value;
}
inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c];
}
inline Bitboard PawnInfo::passed_pawns(Color c) const {
return passedPawns[c];
}
inline int PawnInfo::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f));
}
inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1);
}
inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
template<Color Us>
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq);
}
#endif // !defined(PAWNS_H_INCLUDED)
+113
View File
@@ -0,0 +1,113 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(PLATFORM_H_INCLUDED)
#define PLATFORM_H_INCLUDED
#if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4996) // Function _ftime() may be unsafe
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
# include <inttypes.h>
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
#endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
# include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
typedef pthread_t NativeHandle;
typedef void*(*pt_start_fn)(void*);
# define lock_init(x) pthread_mutex_init(&(x), NULL)
# define lock_grab(x) pthread_mutex_lock(&(x))
# define lock_release(x) pthread_mutex_unlock(&(x))
# define lock_destroy(x) pthread_mutex_destroy(&(x))
# define cond_destroy(x) pthread_cond_destroy(&(x))
# define cond_init(x) pthread_cond_init(&(x), NULL)
# define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW
# include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
#if !defined(NOMINMAX)
# define NOMINMAX // disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// We use critical sections on Windows to support Windows XP and older versions,
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle;
// On Windows 95 and 98 parameter lpThreadId my not be null
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define lock_init(x) InitializeCriticalSection(&(x))
# define lock_grab(x) EnterCriticalSection(&(x))
# define lock_release(x) LeaveCriticalSection(&(x))
# define lock_destroy(x) DeleteCriticalSection(&(x))
# define cond_init(x) { x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(x)
# define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL)
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif
#endif // !defined(PLATFORM_H_INCLUDED)
+626 -835
View File
File diff suppressed because it is too large Load Diff
+129 -179
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#define POSITION_H_INCLUDED
#include <cassert>
#include <cstddef>
#include "bitboard.h"
#include "types.h"
@@ -29,6 +30,7 @@
/// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check.
class Position;
struct Thread;
struct CheckInfo {
@@ -36,20 +38,21 @@ struct CheckInfo {
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[8];
Bitboard checkSq[PIECE_TYPE_NB];
Square ksq;
};
/// The StateInfo struct stores information we need to restore a Position
/// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), an StateInfo object
/// is made on the board (by calling Position::do_move), a StateInfo object
/// must be passed as a parameter.
struct StateInfo {
Key pawnKey, materialKey;
Value npMaterial[2];
Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull;
Score value;
Score psqScore;
Square epSquare;
Key key;
@@ -59,6 +62,11 @@ struct StateInfo {
};
/// When making a move the current StateInfo up to 'key' excluded is copied to
/// the new one. Here we calculate the quad words (64bits) needed to be copied.
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
/// The position data structure. A position consists of the following data:
///
/// * For each piece type, a bitboard representing the squares occupied
@@ -82,64 +90,43 @@ struct StateInfo {
/// * A counter for detecting 50 move rule draws.
class Position {
// No copy c'tor or assignment operator allowed
Position(const Position&);
Position& operator=(const Position&);
public:
Position() {}
Position(const Position& pos, int th) { copy(pos, th); }
Position(const std::string& fen, bool isChess960, int th);
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&);
// Text input/output
void copy(const Position& pos, int th);
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
void set(const std::string& fen, bool isChess960, Thread* th);
const std::string fen() const;
const std::string pretty(Move m = MOVE_NONE) const;
// The piece on a given square
Piece piece_on(Square s) const;
bool square_is_empty(Square s) const;
// Side to move
Color side_to_move() const;
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces(Color c) const;
// Position representation
Bitboard pieces() const;
Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(PieceType pt1, PieceType pt2, Color c) const;
// Number of pieces of each color and type
Bitboard pieces(Color c) const;
Bitboard pieces(Color c, PieceType pt) const;
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
Piece piece_on(Square s) const;
Square king_square(Color c) const;
Square ep_square() const;
bool is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const;
int piece_count(Color c, PieceType pt) const;
// The en passant square
Square ep_square() const;
// Castling
int can_castle(CastleRight f) const;
int can_castle(Color c) const;
bool castle_impeded(Color c, CastlingSide s) const;
Square castle_rook_square(Color c, CastlingSide s) const;
// Current king position for each color
Square king_square(Color c) const;
// Castling rights
bool can_castle(CastleRight f) const;
bool can_castle(Color c) const;
Square castle_rook_square(CastleRight f) const;
// Bitboards for pinned pieces and discovered check candidates
// Checking
Bitboard checkers() const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const;
// Checking pieces and under check information
Bitboard checkers() const;
bool in_check() const;
// Piece lists
const Square* piece_list(Color c, PieceType pt) const;
// Information about attacks to or from a given square
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const;
@@ -149,27 +136,29 @@ public:
// Properties of moves
bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_attacks_square(Move m, Square s) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool is_pseudo_legal(const Move m) const;
bool is_capture(Move m) const;
bool is_capture_or_promotion(Move m) const;
bool is_passed_pawn_push(Move m) const;
// Piece captured with previous moves
Piece piece_moved(Move m) const;
PieceType captured_piece_type() const;
// Information about pawns
// Piece specific
bool pawn_is_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const;
bool opposite_bishops() const;
bool bishop_pair(Color c) const;
// Doing and undoing moves
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
template<bool Do> void do_null_move(StateInfo& st);
void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation
int see(Move m) const;
int see(Move m, int asymmThreshold = 0) const;
int see_sign(Move m) const;
// Accessing hash keys
@@ -178,46 +167,32 @@ public:
Key pawn_key() const;
Key material_key() const;
// Incremental evaluation
Score value() const;
// Incremental piece-square evaluation
Score psq_score() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const;
Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks
bool is_mate() const;
template<bool SkipRepetition> bool is_draw() const;
// Plies from start position to the beginning of search
int startpos_ply_counter() const;
// Other properties of the position
bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const;
Color side_to_move() const;
int game_ply() const;
bool is_chess960() const;
// Current thread ID searching on the position
int thread() const;
Thread* this_thread() const;
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
bool is_draw() const;
// Position consistency check, for debugging
bool pos_is_ok(int* failedStep = NULL) const;
void flip_me();
// Global initialization
static void init();
void flip();
private:
// Initialization helper functions (used while setting up a position)
// Initialization helpers (used while setting up a position)
void clear();
void put_piece(Piece p, Square s);
void set_castle_right(Square ksq, Square rsq);
bool move_is_legal(const Move m) const;
void set_castle_right(Color c, Square rfrom);
// Helper template functions
template<bool Do> void do_castle_move(Move m);
// Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
template<bool FindPinned> Bitboard hidden_checkers() const;
// Computing hash keys from scratch (for initialization and debugging)
@@ -226,43 +201,28 @@ private:
Key compute_material_key() const;
// Computing incremental evaluation scores and material counts
Score pst(Piece p, Square s) const;
Score compute_value() const;
Score compute_psq_score() const;
Value compute_non_pawn_material(Color c) const;
// Board
Piece board[64]; // [square]
// Bitboards
Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color]
Bitboard occupied;
// Piece counts
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Board and pieces
Piece board[SQUARE_NB];
Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[COLOR_NB];
int pieceCount[COLOR_NB][PIECE_TYPE_NB];
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
int index[SQUARE_NB];
// Other info
int castleRightsMask[64]; // [square]
Square castleRookSquare[16]; // [castleRight]
int castleRightsMask[SQUARE_NB];
Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB];
Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB];
StateInfo startState;
int64_t nodes;
int startPosPly;
int gamePly;
Color sideToMove;
int threadID;
Thread* thisThread;
StateInfo* st;
int chess960;
// Static variables
static Score pieceSquareTable[16][64]; // [piece][square]
static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count]
static Key zobEp[64]; // [square]
static Key zobCastle[16]; // [castleRight]
static Key zobSideToMove;
static Key zobExclusion;
};
inline int64_t Position::nodes_searched() const {
@@ -277,7 +237,11 @@ inline Piece Position::piece_on(Square s) const {
return board[s];
}
inline bool Position::square_is_empty(Square s) const {
inline Piece Position::piece_moved(Move m) const {
return board[from_sq(m)];
}
inline bool Position::is_empty(Square s) const {
return board[s] == NO_PIECE;
}
@@ -285,32 +249,28 @@ inline Color Position::side_to_move() const {
return sideToMove;
}
inline Bitboard Position::occupied_squares() const {
return occupied;
}
inline Bitboard Position::empty_squares() const {
return ~occupied;
}
inline Bitboard Position::pieces(Color c) const {
return byColorBB[c];
inline Bitboard Position::pieces() const {
return byTypeBB[ALL_PIECES];
}
inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt];
}
inline Bitboard Position::pieces(PieceType pt, Color c) const {
return byTypeBB[pt] & byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return byTypeBB[pt1] | byTypeBB[pt2];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const {
return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c];
inline Bitboard Position::pieces(Color c) const {
return byColorBB[c];
}
inline Bitboard Position::pieces(Color c, PieceType pt) const {
return byColorBB[c] & byTypeBB[pt];
}
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
}
inline int Position::piece_count(Color c, PieceType pt) const {
@@ -329,16 +289,28 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0];
}
inline bool Position::can_castle(CastleRight f) const {
inline int Position::can_castle(CastleRight f) const {
return st->castleRights & f;
}
inline bool Position::can_castle(Color c) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << c);
inline int Position::can_castle(Color c) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
}
inline Square Position::castle_rook_square(CastleRight f) const {
return castleRookSquare[f];
inline bool Position::castle_impeded(Color c, CastlingSide s) const {
return byTypeBB[ALL_PIECES] & castlePath[c][s];
}
inline Square Position::castle_rook_square(Color c, CastlingSide s) const {
return castleRookSquare[c][s];
}
template<PieceType Pt>
inline Bitboard Position::attacks_from(Square s) const {
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, pieces())
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s];
}
template<>
@@ -346,42 +318,18 @@ inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s];
}
template<PieceType Piece> // Knight and King and white pawns
inline Bitboard Position::attacks_from(Square s) const {
return StepAttacksBB[Piece][s];
}
template<>
inline Bitboard Position::attacks_from<BISHOP>(Square s) const {
return bishop_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<ROOK>(Square s) const {
return rook_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
}
inline Bitboard Position::attacks_from(Piece p, Square s) const {
return attacks_from(p, s, occupied_squares());
return attacks_from(p, s, byTypeBB[ALL_PIECES]);
}
inline Bitboard Position::attackers_to(Square s) const {
return attackers_to(s, occupied_squares());
return attackers_to(s, byTypeBB[ALL_PIECES]);
}
inline Bitboard Position::checkers() const {
return st->checkersBB;
}
inline bool Position::in_check() const {
return st->checkersBB != 0;
}
inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>();
}
@@ -391,7 +339,7 @@ inline Bitboard Position::pinned_pieces() const {
}
inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s));
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
}
inline Key Position::key() const {
@@ -399,7 +347,7 @@ inline Key Position::key() const {
}
inline Key Position::exclusion_key() const {
return st->key ^ zobExclusion;
return st->key ^ Zobrist::exclusion;
}
inline Key Position::pawn_key() const {
@@ -410,16 +358,12 @@ inline Key Position::material_key() const {
return st->materialKey;
}
inline Score Position::pst(Piece p, Square s) const {
return pieceSquareTable[p][s];
inline Score Position::psq_delta(Piece p, Square from, Square to) const {
return pieceSquareTable[p][to] - pieceSquareTable[p][from];
}
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return pieceSquareTable[piece][to] - pieceSquareTable[piece][from];
}
inline Score Position::value() const {
return st->value;
inline Score Position::psq_score() const {
return st->psqScore;
}
inline Value Position::non_pawn_material(Color c) const {
@@ -428,23 +372,29 @@ inline Value Position::non_pawn_material(Color c) const {
inline bool Position::is_passed_pawn_push(Move m) const {
return board[move_from(m)] == make_piece(sideToMove, PAWN)
&& pawn_is_passed(sideToMove, move_to(m));
return type_of(piece_moved(m)) == PAWN
&& pawn_is_passed(sideToMove, to_sq(m));
}
inline int Position::startpos_ply_counter() const {
return startPosPly + st->pliesFromNull; // HACK
inline int Position::game_ply() const {
return gamePly;
}
inline bool Position::opposite_colored_bishops() const {
inline bool Position::opposite_bishops() const {
return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
}
inline bool Position::has_pawn_on_7th(Color c) const {
return pieces(PAWN, c) & rank_bb(relative_rank(c, RANK_7));
inline bool Position::bishop_pair(Color c) const {
return pieceCount[c][BISHOP] >= 2
&& opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]);
}
inline bool Position::pawn_on_7th(Color c) const {
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
}
inline bool Position::is_chess960() const {
@@ -454,22 +404,22 @@ inline bool Position::is_chess960() const {
inline bool Position::is_capture_or_promotion(Move m) const {
assert(is_ok(m));
return is_special(m) ? !is_castle(m) : !square_is_empty(move_to(m));
return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m));
}
inline bool Position::is_capture(Move m) const {
// Note that castle is coded as "king captures the rook"
assert(is_ok(m));
return (!square_is_empty(move_to(m)) && !is_castle(m)) || is_enpassant(m);
return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
}
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
}
inline int Position::thread() const {
return threadID;
inline Thread* Position::this_thread() const {
return thisThread;
}
#endif // !defined(POSITION_H_INCLUDED)
+2 -2
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
/// for white side, for black side the tables are symmetric.
static const Score PSQT[][64] = {
static const Score PSQT[][SQUARE_NB] = {
{ },
{ // Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
+4 -6
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -61,17 +61,15 @@ class RKISS {
return s.d = e + s.a;
}
// Init seed and scramble a few rounds
void raninit() {
public:
RKISS(int seed = 73) {
s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++)
for (int i = 0; i < seed; i++) // Scramble a few rounds
rand64();
}
public:
RKISS() { raninit(); }
template<typename T> T rand() { return T(rand64()); }
};
+851 -1043
View File
File diff suppressed because it is too large Load Diff
+42 -12
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,11 +21,14 @@
#define SEARCH_H_INCLUDED
#include <cstring>
#include <memory>
#include <stack>
#include <vector>
#include "misc.h"
#include "position.h"
#include "types.h"
class Position;
struct SplitPoint;
namespace Search {
@@ -35,16 +38,38 @@ namespace Search {
/// has its own array of Stack objects, indexed by the current ply.
struct Stack {
SplitPoint* sp;
SplitPoint* splitPoint;
int ply;
Move currentMove;
Move excludedMove;
Move bestMove;
Move killers[2];
Depth reduction;
Value eval;
Value staticEval;
Value evalMargin;
int skipNullMove;
int futilityMoveCount;
};
/// RootMove struct is used for moves at the root of the tree. For each root
/// move we store a score, a node count, and a PV (really a refutation in the
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
/// all non-pv moves.
struct RootMove {
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
pv.push_back(m); pv.push_back(MOVE_NONE);
}
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Value score;
Value prevScore;
std::vector<Move> pv;
};
@@ -54,10 +79,10 @@ struct Stack {
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
};
@@ -68,15 +93,20 @@ struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
};
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<Move> SearchMoves;
extern Position RootPosition;
extern std::vector<RootMove> RootMoves;
extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
extern void init();
extern int64_t perft(Position& pos, Depth depth);
extern size_t perft(Position& pos, Depth depth);
extern void think();
} // namespace
} // namespace Search
#endif // !defined(SEARCH_H_INCLUDED)
+263 -383
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,485 +17,365 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::count
#include <cassert>
#include <iostream>
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace Search;
ThreadsManager Threads; // Global object
ThreadPool Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() of the supplied thread. The first
// and last thread are special. First one is the main search thread while the
// last one mimics a timer, they run in main_loop() and timer_loop().
// is launched. It is a wrapper to the virtual function idle_loop().
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID thread) {
#else
void* start_routine(void* thread) {
#endif
Thread* th = (Thread*)thread;
if (th->threadID == 0)
th->main_loop();
else if (th->threadID == MAX_THREADS)
th->timer_loop();
else
th->idle_loop(NULL);
return 0;
}
long start_routine(Thread* th) { th->idle_loop(); return 0; }
} }
// wake_up() wakes up the thread, normally at the beginning of the search or,
// if "sleeping threads" is used, when there is some work to do.
// Thread c'tor starts a newly-created thread of execution that will call
// the the virtual function idle_loop(), going immediately to sleep.
void Thread::wake_up() {
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
lock_grab(&sleepLock);
cond_signal(&sleepCond);
lock_release(&sleepLock);
searching = exit = false;
maxPly = splitPointsSize = 0;
activeSplitPoint = NULL;
activePosition = NULL;
idx = Threads.size();
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
}
// cutoff_occurred() checks whether a beta cutoff has occurred in the current
// active split point, or in some ancestor of the split point.
// Thread d'tor waits for thread termination before to return
Thread::~Thread() {
exit = true; // Search must be already finished
notify_one();
thread_join(handle); // Wait for thread termination
}
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// and then calls check_time(). If msec is 0 thread sleeps until is woken up.
extern void check_time();
void TimerThread::idle_loop() {
while (!exit)
{
mutex.lock();
if (!exit)
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
mutex.unlock();
if (msec)
check_time();
}
}
// MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void MainThread::idle_loop() {
while (true)
{
mutex.lock();
thinking = false;
while (!thinking && !exit)
{
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
sleepCondition.wait(mutex);
}
mutex.unlock();
if (exit)
return;
searching = true;
Search::think();
assert(searching);
searching = false;
}
}
// Thread::notify_one() wakes up the thread when there is some search to do
void Thread::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
void Thread::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff)
return true;
return false;
}
// is_available_to() checks whether the thread is available to help the thread with
// threadID "master" at a split point. An obvious requirement is that thread must be
// idle. With more than two threads, this is not by itself sufficient: If the thread
// is the master of some active split point, it is only available as a slave to the
// threads which are busy searching the split point at the top of "slave"'s split
// point stack (the "helpful master concept" in YBWC terminology).
// Thread::is_available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some split point, it is only available as a slave to the slaves
// which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(int master) const {
bool Thread::is_available_to(Thread* master) const {
if (is_searching)
if (searching)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access.
int localActiveSplitPoints = activeSplitPoints;
int size = splitPointsSize;
// No active split points means that the thread is available as a slave for any
// No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
if ( !localActiveSplitPoints
|| splitPoints[localActiveSplitPoints - 1].is_slave[master])
return true;
return false;
return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx));
}
// read_uci_options() updates number of active threads and other parameters
// according to the UCI options values. It is called before to start a new search.
// init() is called at startup to create and launch requested threads, that will
// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use
// a c'tor becuase Threads is a static object and we need a fully initialized
// engine at this point due to allocation of Endgames in Thread c'tor.
void ThreadsManager::read_uci_options() {
void ThreadPool::init() {
sleepWhileIdle = true;
timer = new TimerThread();
push_back(new MainThread());
read_uci_options();
}
// exit() cleanly terminates the threads before the program exits
void ThreadPool::exit() {
delete timer; // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it)
delete *it;
}
// read_uci_options() updates internal threads parameters from the corresponding
// UCI options and creates/destroys threads to match the requested number. Thread
// objects are dynamically allocated to avoid creating in advance all possible
// threads, with included pawns and material tables, if only few are used.
void ThreadPool::read_uci_options() {
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
size_t requested = Options["Threads"];
set_size(Options["Threads"]);
}
assert(requested > 0);
while (size() < requested)
push_back(new Thread());
// set_size() changes the number of active threads and raises do_sleep flag for
// all the unused threads that will go immediately to sleep.
void ThreadsManager::set_size(int cnt) {
assert(cnt > 0 && cnt <= MAX_THREADS);
activeThreads = cnt;
for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
if (i < activeThreads)
{
// Dynamically allocate pawn and material hash tables according to the
// number of active threads. This avoids preallocating memory for all
// possible threads if only few are used.
threads[i].pawnTable.init();
threads[i].materialTable.init();
threads[i].do_sleep = false;
}
else
threads[i].do_sleep = true;
}
// init() is called during startup. Initializes locks and condition variables
// and launches all threads sending them immediately to sleep.
void ThreadsManager::init() {
// Initialize sleep condition and lock used by thread manager
cond_init(&sleepCond);
lock_init(&threadsLock);
// Initialize thread's sleep conditions and split point locks
for (int i = 0; i <= MAX_THREADS; i++)
while (size() > requested)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
}
// Allocate main thread tables to call evaluate() also when not searching
threads[0].pawnTable.init();
threads[0].materialTable.init();
// Create and launch all the threads, threads will go immediately to sleep
for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].is_searching = false;
threads[i].do_sleep = true;
threads[i].threadID = i;
#if defined(_MSC_VER)
threads[i].handle = CreateThread(NULL, 0, start_routine, &threads[i], 0, NULL);
bool ok = (threads[i].handle != NULL);
#else
bool ok = !pthread_create(&threads[i].handle, NULL, start_routine, &threads[i]);
#endif
if (!ok)
{
std::cerr << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE);
}
delete back();
pop_back();
}
}
// exit() is called to cleanly terminate the threads when the program finishes
// slave_available() tries to find an idle thread which is available as a slave
// for the thread 'master'.
void ThreadsManager::exit() {
Thread* ThreadPool::available_slave(Thread* master) const {
for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].do_terminate = true; // Search must be already finished
threads[i].wake_up();
for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master))
return *it;
// Wait for thread termination
#if defined(_MSC_VER)
WaitForSingleObject(threads[i].handle, 0);
CloseHandle(threads[i].handle);
#else
pthread_join(threads[i].handle, NULL);
#endif
// Now we can safely destroy associated locks and wait conditions
lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&threadsLock);
cond_destroy(&sleepCond);
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID 'master'.
bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++)
if (threads[i].is_available_to(master))
return true;
return false;
}
// split_point_finished() checks if all the slave threads of a given split
// point have finished searching.
bool ThreadsManager::split_point_finished(SplitPoint* sp) const {
for (int i = 0; i < activeThreads; i++)
if (sp->is_slave[i])
return false;
return true;
return NULL;
}
// split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the node
// (because no idle threads are available, or because we have no unused split
// point objects), the function immediately returns. If splitting is possible, a
// SplitPoint object is initialized with all the data that must be copied to the
// helper threads and then helper threads are told that they have been assigned
// work. This will cause them to instantly leave their idle loops and call
// search(). When all threads have returned from search() then split() returns.
// (because no idle threads are available), the function immediately returns.
// If splitting is possible, a SplitPoint object is initialized with all the
// data that must be copied to the helper threads and then helper threads are
// told that they have been assigned work. This will cause them to instantly
// leave their idle loops and call search(). When all threads have returned from
// search() then split() returns.
template <bool Fake>
Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, int nodeType) {
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, Move threatMove, int moveCount,
MovePicker* movePicker, int nodeType) {
assert(pos.pos_is_ok());
assert(bestValue > -VALUE_INFINITE);
assert(bestValue <= alpha);
assert(alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
assert(activeThreads > 1);
int i, master = pos.thread();
Thread& masterThread = threads[master];
// If we already have too many active split points, don't split
if (masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
return bestValue;
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(*bestValue > -VALUE_INFINITE);
assert(depth >= Threads.minimumSplitDepth);
assert(searching);
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
// Pick the next available split point from the split point stack
SplitPoint* sp = &masterThread.splitPoints[masterThread.activeSplitPoints];
SplitPoint& sp = splitPoints[splitPointsSize];
// Initialize the split point
sp->parent = masterThread.splitPoint;
sp->master = master;
sp->is_betaCutoff = false;
sp->depth = depth;
sp->threatMove = threatMove;
sp->alpha = alpha;
sp->beta = beta;
sp->nodeType = nodeType;
sp->bestValue = bestValue;
sp->mp = mp;
sp->moveCount = moveCount;
sp->pos = &pos;
sp->nodes = 0;
sp->ss = ss;
for (i = 0; i < activeThreads; i++)
sp->is_slave[i] = false;
// If we are here it means we are not available
assert(masterThread.is_searching);
int workersCnt = 1; // At least the master is included
sp.masterThread = this;
sp.parentSplitPoint = activeSplitPoint;
sp.slavesMask = 1ULL << idx;
sp.depth = depth;
sp.bestValue = *bestValue;
sp.bestMove = *bestMove;
sp.threatMove = threatMove;
sp.alpha = alpha;
sp.beta = beta;
sp.nodeType = nodeType;
sp.movePicker = movePicker;
sp.moveCount = moveCount;
sp.pos = &pos;
sp.nodes = 0;
sp.cutoff = false;
sp.ss = ss;
// Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent
// 'searching' flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
lock_grab(&threadsLock);
Threads.mutex.lock();
sp.mutex.lock();
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (threads[i].is_available_to(master))
{
workersCnt++;
sp->is_slave[i] = true;
threads[i].splitPoint = sp;
splitPointsSize++;
activeSplitPoint = &sp;
activePosition = NULL;
// This makes the slave to exit from idle_loop()
threads[i].is_searching = true;
size_t slavesCnt = 1; // This thread is always included
Thread* slave;
if (useSleepingThreads)
threads[i].wake_up();
}
lock_release(&threadsLock);
// We failed to allocate even one slave, return
if (!Fake && workersCnt == 1)
return bestValue;
masterThread.splitPoint = sp;
masterThread.activeSplitPoints++;
while ( (slave = Threads.available_slave(this)) != NULL
&& ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake)
{
sp.slavesMask |= 1ULL << slave->idx;
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set.
// We pass the split point as a parameter to the idle loop, which means that
// the thread will return from the idle loop when all slaves have finished
// it will instantly launch a search, because its 'searching' flag is set.
// The thread will return from the idle loop when all slaves have finished
// their work at this split point.
masterThread.idle_loop(sp);
if (slavesCnt > 1 || Fake)
{
sp.mutex.unlock();
Threads.mutex.unlock();
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!masterThread.is_searching);
Thread::idle_loop(); // Force a call to base class idle_loop()
// We have returned from the idle loop, which means that all threads are
// finished. Note that changing state and decreasing activeSplitPoints is done
// under lock protection to avoid a race with Thread::is_available_to().
lock_grab(&threadsLock);
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!searching);
assert(!activePosition);
masterThread.is_searching = true;
masterThread.activeSplitPoints--;
// We have returned from the idle loop, which means that all threads are
// finished. Note that setting 'searching' and decreasing splitPointsSize is
// done under lock protection to avoid a race with Thread::is_available_to().
Threads.mutex.lock();
sp.mutex.lock();
}
lock_release(&threadsLock);
searching = true;
splitPointsSize--;
activeSplitPoint = sp.parentSplitPoint;
activePosition = &pos;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
*bestMove = sp.bestMove;
*bestValue = sp.bestValue;
masterThread.splitPoint = sp->parent;
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
return sp->bestValue;
sp.mutex.unlock();
Threads.mutex.unlock();
}
// Explicit template instantiations
template Value ThreadsManager::split<false>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
template void Thread::split<false>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int);
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
extern void do_timer_event();
// wait_for_think_finished() waits for main thread to go to sleep then returns
void Thread::timer_loop() {
void ThreadPool::wait_for_think_finished() {
while (!do_terminate)
{
lock_grab(&sleepLock);
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
lock_release(&sleepLock);
do_timer_event();
}
MainThread* t = main_thread();
t->mutex.lock();
while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock();
}
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
// milliseconds. If msec is 0 then timer is stopped.
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
// so to start a new search, then returns immediately.
void ThreadsManager::set_timer(int msec) {
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_think_finished();
Thread& timer = threads[MAX_THREADS];
SearchTime = Time::now(); // As early as possible
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
cond_signal(&timer.sleepCond); // Wake up and restart the timer
lock_release(&timer.sleepLock);
}
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
// Thread::main_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() {
while (true)
{
lock_grab(&sleepLock);
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_terminate)
{
cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
cond_wait(&sleepCond, &sleepLock);
}
is_searching = true;
lock_release(&sleepLock);
if (do_terminate)
return;
think(); // This is the search entry point
}
}
// ThreadsManager::start_thinking() is used by UI thread to wake up the main
// thread parked in main_loop() and starting a new search. If asyncMode is true
// then function returns immediately, otherwise caller is blocked waiting for
// the search to finish.
void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode) {
Thread& main = threads[0];
lock_grab(&main.sleepLock);
// Wait main thread has finished before to launch a new search
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
// Copy input arguments to initialize the search
RootPosition.copy(pos, 0);
RootPos = pos;
Limits = limits;
SearchMoves = searchMoves;
SetupStates = states; // Ownership transfer here
RootMoves.clear();
// Reset signals before to start the new search
memset((void*)&Signals, 0, sizeof(Signals));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
// and to wait for the main thread finishing the search. Needed to wait exiting
// and terminate the threads after a 'quit' command.
void ThreadsManager::stop_thinking() {
Thread& main = threads[0];
Search::Signals.stop = true;
lock_grab(&main.sleepLock);
cond_signal(&main.sleepCond); // In case is waiting for stop or ponderhit
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
// is reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void ThreadsManager::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
Thread& main = threads[0];
lock_grab(&main.sleepLock);
while (!Signals.stop)
cond_wait(&main.sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
main_thread()->thinking = true;
main_thread()->notify_one(); // Starts main thread
}
+95 -70
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,42 +20,68 @@
#if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED
#include <cstring>
#include <vector>
#include "lock.h"
#include "material.h"
#include "movepick.h"
#include "pawns.h"
#include "position.h"
#include "search.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t
const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex {
Mutex() { lock_init(l); }
~Mutex() { lock_destroy(l); }
void lock() { lock_grab(l); }
void unlock() { lock_release(l); }
private:
friend struct ConditionVariable;
Lock l;
};
struct ConditionVariable {
ConditionVariable() { cond_init(c); }
~ConditionVariable() { cond_destroy(c); }
void wait(Mutex& m) { cond_wait(c, m.l); }
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
void notify_one() { cond_signal(c); }
private:
WaitCondition c;
};
struct Thread;
struct SplitPoint {
// Const data after splitPoint has been setup
SplitPoint* parent;
// Const data after split point has been setup
const Position* pos;
const Search::Stack* ss;
Thread* masterThread;
Depth depth;
Value beta;
int nodeType;
int ply;
int master;
Move threatMove;
// Const pointers to shared data
MovePicker* mp;
Search::Stack* ss;
MovePicker* movePicker;
SplitPoint* parentSplitPoint;
// Shared data
Lock lock;
Mutex mutex;
volatile uint64_t slavesMask;
volatile int64_t nodes;
volatile Value alpha;
volatile Value bestValue;
volatile Move bestMove;
volatile int moveCount;
volatile bool is_betaCutoff;
volatile bool is_slave[MAX_THREADS];
volatile bool cutoff;
};
@@ -66,77 +92,76 @@ struct SplitPoint {
struct Thread {
void wake_up();
Thread();
virtual ~Thread();
virtual void idle_loop();
void notify_one();
bool cutoff_occurred() const;
bool is_available_to(int master) const;
void idle_loop(SplitPoint* sp);
void main_loop();
void timer_loop();
bool is_available_to(Thread* master) const;
void wait_for(volatile const bool& b);
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int threadID;
template <bool Fake>
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Material::Table materialTable;
Endgames endgames;
Pawns::Table pawnsTable;
Position* activePosition;
size_t idx;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints;
volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_terminate;
#if defined(_MSC_VER)
HANDLE handle;
#else
pthread_t handle;
#endif
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize;
volatile bool searching;
volatile bool exit;
};
/// ThreadsManager class handles all the threads related stuff like init, starting,
/// MainThread and TimerThread are sublassed from Thread to characterize the two
/// special threads: the main one and the recurring timer.
struct MainThread : public Thread {
MainThread() : thinking(true) {} // Avoid a race with start_thinking()
virtual void idle_loop();
volatile bool thinking;
};
struct TimerThread : public Thread {
TimerThread() : msec(0) {}
virtual void idle_loop();
int msec;
};
/// ThreadPool struct handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
need to explicitly initialize to zero its data members because variables with
static storage duration are automatically set to zero before enter main()
*/
public:
Thread& operator[](int threadID) { return threads[threadID]; }
void init();
void exit();
struct ThreadPool : public std::vector<Thread*> {
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime.
void set_size(int cnt);
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options();
bool available_slave_exists(int master) const;
bool split_point_finished(SplitPoint* sp) const;
void set_timer(int msec);
void wait_for_stop_or_ponderhit();
void stop_thinking();
void start_thinking(const Position& pos, const Search::LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode);
Thread* available_slave(Thread* master) const;
void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&);
template <bool Fake>
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
friend struct Thread;
Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
Lock threadsLock;
bool sleepWhileIdle;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
int activeThreads;
bool useSleepingThreads;
WaitCondition sleepCond;
size_t maxThreadsPerSplitPoint;
Mutex mutex;
ConditionVariable sleepCondition;
TimerThread* timer;
};
extern ThreadsManager Threads;
extern ThreadPool Threads;
#endif // !defined(THREAD_H_INCLUDED)
+13 -13
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
#include <cmath>
#include <algorithm>
#include "misc.h"
#include "search.h"
#include "timeman.h"
#include "ucioption.h"
@@ -30,7 +29,7 @@ namespace {
/// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio
const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
@@ -73,7 +72,7 @@ namespace {
enum TimeType { OptimumTime, MaxTime };
template<TimeType>
int remaining(int myTime, int movesToGo, int fullMoveNumber);
int remaining(int myTime, int movesToGo, int fullMoveNumber, int slowMover);
}
@@ -84,7 +83,7 @@ void TimeManager::pv_instability(int curChanges, int prevChanges) {
}
void TimeManager::init(const Search::LimitsType& limits, int currentPly)
void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
{
/* We support four different kind of time controls:
@@ -108,25 +107,26 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly)
int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"];
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = limits.time;
optimumSearchTime = maximumSearchTime = limits.time[us];
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? std::min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly, slowMover);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly, slowMover);
optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = std::min(maximumSearchTime, t2);
@@ -143,12 +143,12 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly)
namespace {
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly)
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
{
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly);
int thisMoveImportance = move_importance(currentPly) * slowMover / 100;
int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++)
+2 -2
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
class TimeManager {
public:
void init(const Search::LimitsType& limits, int currentPly);
void init(const Search::LimitsType& limits, int currentPly, Color us);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
+28 -50
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,49 +20,37 @@
#include <cstring>
#include <iostream>
#include "bitboard.h"
#include "tt.h"
TranspositionTable TT; // Our global transposition table
TranspositionTable::TranspositionTable() {
size = generation = 0;
entries = NULL;
}
TranspositionTable::~TranspositionTable() {
delete [] entries;
}
/// TranspositionTable::set_size() sets the size of the transposition table,
/// measured in megabytes.
/// measured in megabytes. Transposition table consists of a power of 2 number
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1024;
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
// Transposition table consists of clusters and each cluster consists
// of ClusterSize number of TTEntries. Each non-empty entry contains
// information of exactly one position and newSize is the number of
// clusters we are going to allocate.
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2;
uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));
if (newSize == size)
if (hashMask == size - ClusterSize)
return;
size = newSize;
delete [] entries;
entries = new (std::nothrow) TTCluster[size];
if (!entries)
hashMask = size - ClusterSize;
delete [] table;
table = new (std::nothrow) TTEntry[size];
if (!table)
{
std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();
clear(); // Operator new is not guaranteed to initialize memory to zero
}
@@ -72,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) {
void TranspositionTable::clear() {
memset(entries, 0, size * sizeof(TTCluster));
memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
}
@@ -84,35 +72,35 @@ void TranspositionTable::clear() {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) {
void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) {
int c1, c2, c3;
TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster
uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster
tte = replace = first_entry(posKey);
tte = replace = first_entry(key);
for (int i = 0; i < ClusterSize; i++, tte++)
for (unsigned i = 0; i < ClusterSize; i++, tte++)
{
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old
if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{
// Preserve any existing ttMove
if (m == MOVE_NONE)
m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD);
tte->save(key32, v, t, d, m, generation, statV, kingD);
return;
}
// Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == VALUE_TYPE_EXACT ? -2 : 0);
c2 = (tte->generation() == generation || tte->type() == BOUND_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
replace = tte;
}
replace->save(posKey32, v, t, d, m, generation, statV, kingD);
replace->save(key32, v, t, d, m, generation, statV, kingD);
}
@@ -120,24 +108,14 @@ void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d,
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::probe(const Key posKey) const {
TTEntry* TranspositionTable::probe(const Key key) const {
uint32_t posKey32 = posKey >> 32;
TTEntry* tte = first_entry(posKey);
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (int i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == posKey32)
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
}
/// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from
/// entries from the current search.
void TranspositionTable::new_search() {
generation++;
}
+32 -80
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,12 +20,9 @@
#if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED
#include <iostream>
#include "misc.h"
#include "types.h"
/// The TTEntry is the class of transposition table entries
///
/// A TTEntry needs 128 bits to be stored
@@ -47,71 +44,60 @@
class TTEntry {
public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value statM) {
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
key32 = (uint32_t)k;
move16 = (uint16_t)m;
valueType = (uint8_t)t;
bound = (uint8_t)b;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
staticValue = (int16_t)statV;
staticMargin = (int16_t)statM;
evalValue = (int16_t)ev;
evalMargin = (int16_t)em;
}
void set_generation(int g) { generation8 = (uint8_t)g; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
ValueType type() const { return (ValueType)valueType; }
int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; }
Value static_value_margin() const { return (Value)staticMargin; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
Bound type() const { return (Bound)bound; }
int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; }
Value eval_margin() const { return (Value)evalMargin; }
private:
uint32_t key32;
uint16_t move16;
uint8_t valueType, generation8;
int16_t value16, depth16, staticValue, staticMargin;
uint8_t bound, generation8;
int16_t value16, depth16, evalValue, evalMargin;
};
/// This is the number of TTEntry slots for each cluster
const int ClusterSize = 4;
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster
/// must not be bigger than a cache line size. In case it is less, it should
/// be padded to guarantee always aligned accesses.
struct TTCluster {
TTEntry data[ClusterSize];
};
/// The transposition table class. This is basically just a huge array containing
/// TTCluster objects, and a few methods for writing and reading entries.
/// A TranspositionTable consists of a power of 2 number of clusters and each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
/// contains information of exactly one position. Size of a cluster shall not be
/// bigger than a cache line size. In case it is less, it should be padded to
/// guarantee always aligned accesses.
class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
static const unsigned ClusterSize = 4; // A cluster is 64 Bytes
public:
TranspositionTable();
~TranspositionTable();
~TranspositionTable() { delete [] table; }
void new_search() { generation++; }
TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const;
void set_size(size_t mbSize);
void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD);
TTEntry* probe(const Key posKey) const;
void new_search();
TTEntry* first_entry(const Key posKey) const;
void refresh(const TTEntry* tte) const;
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
private:
size_t size;
TTCluster* entries;
uint32_t hashMask;
TTEntry* table;
uint8_t generation; // Size must be not bigger then TTEntry::generation8
};
@@ -122,9 +108,9 @@ extern TranspositionTable TT;
/// a cluster given a position. The lowest order bits of the key are used to
/// get the index of the cluster.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
inline TTEntry* TranspositionTable::first_entry(const Key key) const {
return entries[((uint32_t)posKey) & (size - 1)].data;
return table + ((uint32_t)key & hashMask);
}
@@ -136,38 +122,4 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
/// A simple fixed size hash table used to store pawns and material
/// configurations. It is basically just an array of Entry objects.
/// Without cluster concept or overwrite policy.
template<class Entry, int HashSize>
struct SimpleHash {
typedef SimpleHash<Entry, HashSize> Base;
void init() {
if (entries)
return;
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
<< " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
}
virtual ~SimpleHash() { delete [] entries; }
Entry* probe(Key key) const { return entries + ((uint32_t)key & (HashSize - 1)); }
void prefetch(Key key) const { ::prefetch((char*)probe(key)); }
protected:
Entry* entries;
};
#endif // !defined(TT_H_INCLUDED)
+118 -138
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,37 +34,15 @@
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
///
/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW)
/// | Locks and Condition Variables: these are not supported by
/// | Windows XP and older, to compile for those platforms you
/// | should enable OLD_LOCKS.
#include <cassert>
#include <cctype>
#include <climits>
#include <cstdlib>
#if defined(_MSC_VER)
#include "platform.h"
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
# include <inttypes.h>
#endif
#if defined(_WIN64)
#if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
@@ -74,6 +52,10 @@ typedef unsigned __int64 uint64_t;
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
# endif
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
# define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else
@@ -103,7 +85,7 @@ const bool Is64Bit = false;
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int MAX_MOVES = 256;
const int MAX_MOVES = 192;
const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
@@ -142,22 +124,33 @@ enum Move {
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
enum MoveType {
NORMAL = 0,
PROMOTION = 1 << 14,
ENPASSANT = 2 << 14,
CASTLE = 3 << 14
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
enum CastleRight {
enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
WHITE_OOO = 2,
BLACK_OO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
ALL_CASTLES = 15,
CASTLE_RIGHT_NB = 16
};
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE,
CASTLING_SIDE_NB = 2
};
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
};
enum ScaleFactor {
@@ -167,11 +160,11 @@ enum ScaleFactor {
SCALE_FACTOR_NONE = 255
};
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1,
VALUE_TYPE_LOWER = 2,
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
enum Bound {
BOUND_NONE = 0,
BOUND_UPPER = 1,
BOUND_LOWER = 2,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
};
enum Value {
@@ -186,22 +179,30 @@ enum Value {
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1278,
QueenValueMg = 2521, QueenValueEg = 2558
};
enum PieceType {
NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
NO_PIECE_TYPE = 0, ALL_PIECES = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6,
PIECE_TYPE_NB = 8
};
enum Piece {
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
NO_PIECE = 0,
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14,
PIECE_NB = 16
};
enum Color {
WHITE, BLACK, NO_COLOR
WHITE, BLACK, NO_COLOR, COLOR_NB = 2
};
enum Depth {
@@ -211,7 +212,7 @@ enum Depth {
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -4 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY
};
@@ -227,6 +228,8 @@ enum Square {
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
SQUARE_NB = 64,
DELTA_N = 8,
DELTA_E = 1,
DELTA_S = -8,
@@ -241,11 +244,11 @@ enum Square {
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8
};
@@ -321,23 +324,52 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i);
}
/// Weight score v by score w trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
const Value KnightValueMidgame = Value(0x331);
const Value KnightValueEndgame = Value(0x34E);
const Value BishopValueMidgame = Value(0x344);
const Value BishopValueEndgame = Value(0x359);
const Value RookValueMidgame = Value(0x4F6);
const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE);
namespace Zobrist {
extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
extern int SquareDistance[64][64];
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key enpassant[FILE_NB];
extern Key castle[CASTLE_RIGHT_NB];
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
extern Value PieceValue[PHASE_NB][PIECE_NB];
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
inline Color operator~(Color c) {
return Color(c ^ 1);
}
inline Square operator~(Square s) {
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
}
inline Square operator|(File f, Rank r) {
return Square((r << 3) | f);
}
inline Value mate_in(int ply) {
return VALUE_MATE - ply;
@@ -351,23 +383,20 @@ inline Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) | pt);
}
inline CastleRight make_castle_right(Color c, CastlingSide s) {
return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
}
inline PieceType type_of(Piece p) {
return PieceType(p & 7);
}
inline Color color_of(Piece p) {
assert(p != NO_PIECE);
return Color(p >> 3);
}
inline Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
}
inline bool square_is_ok(Square s) {
inline bool is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
@@ -379,12 +408,8 @@ inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) {
return Square(s ^ 7);
return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1
}
inline Square relative_square(Color c, Square s) {
@@ -400,7 +425,7 @@ inline Rank relative_rank(Color c, Square s) {
}
inline bool opposite_colors(Square s1, Square s2) {
int s = s1 ^ s2;
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
@@ -416,47 +441,31 @@ inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char piece_type_to_char(PieceType pt) {
return " PNBRQK"[pt];
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
inline char file_to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A'));
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
return char(r - RANK_1 + '1');
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
inline Square move_from(Move m) {
inline Square from_sq(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square move_to(Move m) {
inline Square to_sq(Move m) {
return Square(m & 0x3F);
}
inline bool is_special(Move m) {
return m & (3 << 14);
inline MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
inline bool is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int is_enpassant(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline PieceType promotion_piece_type(Move m) {
inline PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
@@ -464,20 +473,13 @@ inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(to | (from << 6) | (1 << 14) | ((promotion - 2) << 12)) ;
}
inline Move make_enpassant_move(Square from, Square to) {
return Move(to | (from << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(to | (from << 6) | (3 << 14));
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12));
}
inline bool is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NULL and MOVE_NONE
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
}
#include <string>
@@ -487,26 +489,4 @@ inline const std::string square_to_string(Square s) {
return ch;
}
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
#endif // !defined(TYPES_H_INCLUDED)
+86 -133
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,13 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "evaluate.h"
#include "misc.h"
#include "notation.h"
#include "position.h"
#include "search.h"
#include "thread.h"
@@ -31,20 +31,20 @@
using namespace std;
extern void benchmark(const Position& pos, istream& is);
namespace {
// FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// Keep track of position keys along the setup moves (from start position to the
// position just before to start searching). This is needed by draw detection
// where, due to 50 moves rule, we need to check at most 100 plies back.
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
// position just before to start searching). Needed by repetition draw detection.
Search::StateStackPtr SetupStates;
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
void go(Position& pos, istringstream& up);
void perft(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up);
}
@@ -53,85 +53,79 @@ namespace {
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// commands, the function also supports a few debug commands.
void uci_loop() {
void UCI::loop(const string& args) {
Position pos(StartFEN, false, 0); // The root position
string cmd, token;
Position pos(StartFEN, false, Threads.main_thread()); // The root position
string token, cmd = args;
while (token != "quit")
{
if (!getline(cin, cmd)) // Block here waiting for input
do {
if (args.empty() && !getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop")
Threads.stop_thinking();
else if (token == "ponderhit")
if (token == "quit" || token == "stop" || token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
Threads.stop_thinking();
// GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switching from pondering to normal search.
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main_thread()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
pos.from_fen(StartFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "perft")
perft(pos, is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip_me();
else if (token == "eval")
else if (token == "perft" && (is >> token)) // Read perft depth
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
}
stringstream ss;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << token << " current perft";
benchmark(pos, ss);
}
else if (token == "key")
cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << endl;
sync_cout << hex << uppercase << setfill('0')
<< "position key: " << setw(16) << pos.key()
<< "\nmaterial key: " << setw(16) << pos.material_key()
<< "\npawn key: " << setw(16) << pos.pawn_key()
<< dec << sync_endl;
else if (token == "uci")
cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << endl;
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is);
else if (token == "position") set_position(pos, is);
else if (token == "setoption") set_option(is);
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else
cout << "Unknown command: " << cmd << endl;
}
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
Threads.wait_for_think_finished(); // Cannot quit while search is running
}
namespace {
// set_position() is called when engine receives the "position" UCI
// command. The function sets up the position described in the given
// fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves").
// set_position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given fen string ("fen")
// or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves").
void set_position(Position& pos, istringstream& is) {
@@ -151,16 +145,14 @@ namespace {
else
return;
pos.from_fen(fen, Options["UCI_Chess960"]);
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any)
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
{
pos.do_move(m, *SetupState);
// Increment pointer to StateRingBuf circular buffer
if (++SetupState - StateRingBuf >= 102)
SetupState = StateRingBuf;
SetupStates->push(StateInfo());
pos.do_move(m, SetupStates->top());
}
}
@@ -182,81 +174,42 @@ namespace {
while (is >> token)
value += string(" ", !value.empty()) + token;
if (!Options.count(name))
cout << "No such option: " << name << endl;
else if (value.empty()) // UCI buttons don't have a value
Options[name] = true;
else
if (Options.count(name))
Options[name] = value;
else
sync_cout << "No such option: " << name << sync_endl;
}
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts
// the main searching thread.
// the thinking time and other parameters from the input string, and starts
// the search.
void go(Position& pos, istringstream& is) {
void go(const Position& pos, istringstream& is) {
string token;
Search::LimitsType limits;
std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
vector<Move> searchMoves;
string token;
while (is >> token)
{
if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "wtime")
is >> time[WHITE];
else if (token == "btime")
is >> time[BLACK];
else if (token == "winc")
is >> inc[WHITE];
else if (token == "binc")
is >> inc[BLACK];
else if (token == "movestogo")
is >> limits.movesToGo;
else if (token == "depth")
is >> limits.maxDepth;
else if (token == "nodes")
is >> limits.maxNodes;
else if (token == "movetime")
is >> limits.maxTime;
else if (token == "searchmoves")
if (token == "searchmoves")
while (is >> token)
searchMoves.push_back(move_from_uci(pos, token));
else if (token == "wtime") is >> limits.time[WHITE];
else if (token == "btime") is >> limits.time[BLACK];
else if (token == "winc") is >> limits.inc[WHITE];
else if (token == "binc") is >> limits.inc[BLACK];
else if (token == "movestogo") is >> limits.movestogo;
else if (token == "depth") is >> limits.depth;
else if (token == "nodes") is >> limits.nodes;
else if (token == "movetime") is >> limits.movetime;
else if (token == "mate") is >> limits.mate;
else if (token == "infinite") limits.infinite = true;
else if (token == "ponder") limits.ponder = true;
}
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
Threads.start_thinking(pos, limits, searchMoves, true);
}
// perft() is called when engine receives the "perft" command. The function
// calls perft() with the required search depth then prints counted leaf nodes
// and elapsed time.
void perft(Position& pos, istringstream& is) {
int depth, time;
if (!(is >> depth))
return;
time = system_time();
int64_t n = Search::perft(pos, depth * ONE_PLY);
time = system_time() - time;
std::cout << "\nNodes " << n
<< "\nTime (ms) " << time
<< "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
Threads.start_thinking(pos, limits, searchMoves, SetupStates);
}
}
+89 -49
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,74 +18,90 @@
*/
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <sstream>
#include "evaluate.h"
#include "misc.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
using std::string;
OptionsMap Options; // Global object
UCI::OptionsMap Options; // Global object
namespace UCI {
/// 'On change' actions, triggered by an option's value change
void on_logger(const Option& o) { start_logger(o); }
void on_eval(const Option&) { Eval::init(); }
void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.set_size(o); }
void on_clear_hash(const Option&) { TT.clear(); }
/// Our case insensitive less() function as required by UCI protocol
static bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
}
/// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Min Split Depth"
/// init() initializes the UCI options to their hard coded default values
/// and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
OptionsMap::OptionsMap() {
void init(OptionsMap& o) {
int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Min Split Depth"] = UCIOption(msd, 4, 7);
o["Max Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
o["Use Debug Log"] = Option(false, on_logger);
o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false);
o["Contempt Factor"] = Option(0, -50, 50);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(false);
o["Hash"] = Option(32, 1, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true);
o["OwnBook"] = Option(false);
o["MultiPV"] = Option(1, 1, 500);
o["Skill Level"] = Option(20, 0, 20);
o["Emergency Move Horizon"] = Option(40, 0, 50);
o["Emergency Base Time"] = Option(200, 0, 30000);
o["Emergency Move Time"] = Option(70, 0, 5000);
o["Minimum Thinking Time"] = Option(20, 0, 5000);
o["Slow Mover"] = Option(100, 10, 1000);
o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false, on_eval);
}
/// operator<<() is used to output all the UCI options in chronological insertion
/// order (the idx field) and in the format defined by the UCI protocol.
/// operator<<() is used to print all the options default values in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); idx++)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx)
{
const UCIOption& o = it->second;
const Option& o = it->second;
os << "\noption name " << it->first << " type " << o.type;
if (o.type != "button")
@@ -100,28 +116,52 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
}
/// UCIOption class c'tors
/// Option c'tors and conversion operators
UCIOption::UCIOption(const char* v) : type("string"), min(0), max(0), idx(Options.size())
Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f)
{ defaultValue = currentValue = v; }
UCIOption::UCIOption(bool v, string t) : type(t), min(0), max(0), idx(Options.size())
Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f)
{ defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int v, int minv, int maxv) : type("spin"), min(minv), max(maxv), idx(Options.size())
Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f)
{}
Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f)
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
/// check for option's limits, but we could receive the new value directly from
/// the user by teminal window, so let's check the bounds anyway.
Option::operator int() const {
assert(type == "check" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
void UCIOption::operator=(const string& v) {
Option::operator std::string() const {
assert(type == "string");
return currentValue;
}
/// operator=() updates currentValue and triggers on_change() action. It's up to
/// the GUI to check for option's limits, but we could receive the new value from
/// the user by console window, so let's check the bounds anyway.
Option& Option::operator=(const string& v) {
assert(!type.empty());
if ( !v.empty()
&& (type == "check" || type == "button") == (v == "true" || v == "false")
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
if ( (type != "button" && v.empty())
|| (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
return *this;
if (type != "button")
currentValue = v;
if (on_change)
(*on_change)(*this);
return *this;
}
} // namespace UCI
+29 -34
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,33 +20,35 @@
#if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED
#include <cassert>
#include <cstdlib>
#include <map>
#include <string>
struct OptionsMap;
namespace UCI {
class Option;
/// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a std::map
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
/// Option class implements an option as defined by UCI protocol
class Option {
typedef void (Fn)(const Option&);
/// UCIOption class implements an option as defined by UCI protocol
class UCIOption {
public:
UCIOption() {} // Required by std::map::operator[]
UCIOption(const char* v);
UCIOption(bool v, std::string type = "check");
UCIOption(int v, int min, int max);
Option(Fn* = NULL);
Option(bool v, Fn* = NULL);
Option(const char* v, Fn* = NULL);
Option(int v, int min, int max, Fn* = NULL);
void operator=(const std::string& v);
void operator=(bool v) { *this = std::string(v ? "true" : "false"); }
operator int() const {
assert(type == "check" || type == "button" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
operator std::string() const {
assert(type == "string");
return currentValue;
}
Option& operator=(const std::string& v);
operator int() const;
operator std::string() const;
private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
@@ -54,21 +56,14 @@ private:
std::string defaultValue, currentValue, type;
int min, max;
size_t idx;
Fn* on_change;
};
void init(OptionsMap&);
void loop(const std::string&);
/// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
} // namespace UCI
/// Our options container is actually a map with a customized c'tor
struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
OptionsMap();
};
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
extern OptionsMap Options;
extern UCI::OptionsMap Options;
#endif // !defined(UCIOPTION_H_INCLUDED)