Compare commits

...

274 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
45 changed files with 2149 additions and 2413 deletions
+17 -24
View File
@@ -1,5 +1,4 @@
1. Introduction
---------------
### 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
@@ -8,46 +7,41 @@ 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 32 CPUs, but has not been
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
*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.
recommended to raise the value of the *Min Split Depth* UCI parameter to 7.
2. Files
--------
### Files
This distribution of Stockfish consists of the following files:
* Readme.txt, the file you are currently reading.
* 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 4 below.
* 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.
3. Opening books
----------------
### 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"
documentation. The book file can be selected by setting the *Book File*
UCI parameter.
4. Compiling it yourself
------------------------
### Compiling it yourself
On Unix-like systems, it should be possible to compile Stockfish
directly from the source code with the included Makefile.
@@ -55,17 +49,16 @@ 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
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"
set/unset some switches in the compiler command line; see file *types.h*
for a quick reference.
5. Terms of use
---------------
### Terms of use
Stockfish is free, and distributed under the GNU General Public License
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
@@ -78,4 +71,4 @@ 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.
*Copying.txt*
+2 -2
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
@@ -29,7 +29,7 @@ Cowardice = 100
Min Split Depth = 4
Max Threads per Split Point = 5
Threads = 1
Use Sleeping Threads = true
Use Sleeping Threads = false
Hash = 128
Ponder = true
OwnBook = false
+42 -7
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
@@ -58,6 +58,7 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
# 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
@@ -77,6 +78,7 @@ ifeq ($(ARCH),general-64)
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),general-32)
@@ -86,6 +88,7 @@ ifeq ($(ARCH),general-32)
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
# x86-section
@@ -96,6 +99,7 @@ ifeq ($(ARCH),x86-64)
prefetch = yes
bsfq = yes
popcnt = no
sse = yes
endif
ifeq ($(ARCH),x86-64-modern)
@@ -105,6 +109,7 @@ ifeq ($(ARCH),x86-64-modern)
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
endif
ifeq ($(ARCH),x86-32)
@@ -114,6 +119,7 @@ ifeq ($(ARCH),x86-32)
prefetch = yes
bsfq = no
popcnt = no
sse = yes
endif
ifeq ($(ARCH),x86-32-old)
@@ -123,6 +129,18 @@ ifeq ($(ARCH),x86-32-old)
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
@@ -133,6 +151,7 @@ ifeq ($(ARCH),osx-ppc-64)
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),osx-ppc-32)
@@ -142,6 +161,7 @@ ifeq ($(ARCH),osx-ppc-32)
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),osx-x86-64)
@@ -151,6 +171,7 @@ ifeq ($(ARCH),osx-x86-64)
prefetch = yes
bsfq = yes
popcnt = no
sse = yes
endif
ifeq ($(ARCH),osx-x86-32)
@@ -160,6 +181,7 @@ ifeq ($(ARCH),osx-x86-32)
prefetch = yes
bsfq = no
popcnt = no
sse = yes
endif
@@ -220,7 +242,7 @@ 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)
@@ -228,12 +250,16 @@ ifeq ($(comp),clang)
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.0
endif
### 3.3 General linker settings
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
@@ -243,7 +269,7 @@ ifneq ($(comp),mingw)
endif
ifeq ($(os),osx)
LDFLAGS += -arch $(arch)
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.0
endif
### 3.4 Debugging
@@ -265,6 +291,10 @@ ifeq ($(optimize),yes)
CXXFLAGS += -mdynamic-no-pic
endif
endif
ifeq ($(arch),armv7)
CXXFLAGS += -fno-gcse
endif
endif
ifeq ($(comp),mingw)
@@ -301,8 +331,10 @@ endif
### 3.7 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
ifeq ($(sse),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
endif
else
CXXFLAGS += -DNO_PREFETCH
endif
@@ -360,6 +392,7 @@ help:
@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 ""
@@ -438,6 +471,7 @@ config-sanity:
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo "sse: '$(sse)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@@ -449,12 +483,13 @@ 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 "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(sse)" = "yes" || test "$(sse)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
$(EXE): $(OBJS)
+10 -7
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
@@ -66,7 +66,7 @@ void benchmark(const Position& current, istream& is) {
vector<string> fens;
// Assign default values to missing arguments
string ttSize = (is >> token) ? token : "128";
string ttSize = (is >> token) ? token : "32";
string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "12";
string fenFile = (is >> token) ? token : "default";
@@ -82,6 +82,9 @@ void benchmark(const Position& current, istream& is) {
else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str());
else if (limitType == "mate")
limits.mate = atoi(limit.c_str());
else
limits.depth = atoi(limit.c_str());
@@ -89,7 +92,7 @@ void benchmark(const Position& current, istream& is) {
fens.assign(Defaults, Defaults + 16);
else if (fenFile == "current")
fens.push_back(current.to_fen());
fens.push_back(current.fen());
else
{
@@ -99,7 +102,7 @@ void benchmark(const Position& current, istream& is) {
if (!file.is_open())
{
cerr << "Unable to open file " << fenFile << endl;
exit(EXIT_FAILURE);
return;
}
while (getline(file, fen))
@@ -127,9 +130,9 @@ void benchmark(const Position& current, istream& is) {
}
else
{
Threads.start_searching(pos, limits, vector<Move>(), st);
Threads.wait_for_search_finished();
nodes += Search::RootPosition.nodes_searched();
Threads.start_thinking(pos, limits, vector<Move>(), st);
Threads.wait_for_think_finished();
nodes += Search::RootPos.nodes_searched();
}
}
+87 -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
@@ -18,12 +18,32 @@
*/
#include <cassert>
#include <vector>
#include "bitboard.h"
#include "types.h"
namespace {
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
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];
// 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,
@@ -35,196 +55,119 @@ namespace {
struct KPKPosition {
Result classify_leaf(int idx);
Result classify(int idx, Result db[]);
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 Result db[]) const;
template<Color Us> Result classify(const std::vector<KPKPosition>& db);
template<Color Us> Bitboard k_attacks() const {
return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq];
}
Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; }
void decode_index(int idx);
Square wksq, bksq, psq;
Color stm;
Color us;
Square bksq, wksq, psq;
Result res;
};
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // stm * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32];
int index(Square wksq, Square bksq, Square psq, Color stm);
}
} // namespace
uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
int idx = index(wksq, bksq, wpsq, stm);
return KPKBitbase[idx / 32] & (1 << (idx & 31));
assert(file_of(wpsq) <= FILE_D);
unsigned idx = index(us, bksq, wksq, wpsq);
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
}
void Bitbases::init_kpk() {
Result db[IndexMax];
KPKPosition pos;
int idx, bit, repeat = 1;
unsigned idx, repeat = 1;
std::vector<KPKPosition> db(IndexMax);
// Initialize table with known win / draw positions
// Initialize db with known win / draw positions
for (idx = 0; idx < IndexMax; idx++)
db[idx] = pos.classify_leaf(idx);
db[idx].classify_leaf(idx);
// 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 = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN)
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax / 32; idx++)
for (bit = 0; bit < 32; bit++)
if (db[32 * idx + bit] == WIN)
KPKBitbase[idx] |= 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 index(Square w, Square b, Square p, Color c) {
assert(file_of(p) <= FILE_D);
return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15);
}
void KPKPosition::decode_index(int idx) {
stm = Color(idx & 1);
bksq = Square((idx >> 1) & 63);
wksq = Square((idx >> 7) & 63);
psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
}
Result KPKPosition::classify_leaf(int idx) {
decode_index(idx);
wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
us = Color((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15));
// Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq
|| (k_attacks<WHITE>() & bksq)
|| (stm == WHITE && (p_attacks() & bksq)))
return INVALID;
|| (StepAttacksBB[KING][wksq] & bksq)
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
return res = 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(psq) == RANK_7
&& stm == WHITE
&& wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1
||(k_attacks<WHITE>() & (psq + DELTA_N))))
return WIN;
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;
// Check for known draw positions
//
// Case 1: Stalemate
if ( stm == BLACK
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | p_attacks())))
return DRAW;
// Case 2: King can capture undefended pawn
if ( stm == BLACK
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
return DRAW;
// Case 3: Black king in front of white pawn
if ( bksq == psq + DELTA_N
&& rank_of(psq) < RANK_7)
return DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( stm == WHITE
&& wksq == psq + DELTA_N
&& bksq == wksq + DELTA_N + DELTA_N
&& rank_of(psq) < RANK_5)
return DRAW;
// Case 5: Stalemate with rook pawn
if ( bksq == SQ_A8
&& file_of(psq) == FILE_A)
return DRAW;
// Case 6: White king trapped on the rook file
if ( file_of(wksq) == FILE_A
&& file_of(psq) == FILE_A
&& rank_of(wksq) > rank_of(psq)
&& bksq == wksq + 2)
return DRAW;
return UNKNOWN;
return res = UNKNOWN;
}
template<Color Us>
Result KPKPosition::classify(const Result db[]) const {
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to Move: 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.
// 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.
//
// Black to Move: 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 RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
// 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.
Result r = INVALID;
Bitboard b = k_attacks<Us>();
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b)
{
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
if (Us == WHITE && (r & WIN))
return WIN;
if (Us == BLACK && (r & DRAW))
return DRAW;
}
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)
{
Square s = psq + DELTA_N;
r |= db[index(wksq, bksq, s, BLACK)]; // Single push
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push
if (r & WIN)
return WIN;
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
}
return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN;
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(int idx, Result db[]) {
decode_index(idx);
return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(db);
}
}
} // namespace
+29 -35
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
@@ -28,45 +28,44 @@
CACHE_LINE_ALIGNMENT
Bitboard RMasks[64];
Bitboard RMagics[64];
Bitboard* RAttacks[64];
unsigned RShifts[64];
Bitboard RMasks[SQUARE_NB];
Bitboard RMagics[SQUARE_NB];
Bitboard* RAttacks[SQUARE_NB];
unsigned RShifts[SQUARE_NB];
Bitboard BMasks[64];
Bitboard BMagics[64];
Bitboard* BAttacks[64];
unsigned BShifts[64];
Bitboard BMasks[SQUARE_NB];
Bitboard BMagics[SQUARE_NB];
Bitboard* BAttacks[SQUARE_NB];
unsigned BShifts[SQUARE_NB];
Bitboard SquareBB[64];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard AdjacentFilesBB[8];
Bitboard ThisAndAdjacentFilesBB[8];
Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64];
Bitboard DistanceRingsBB[64][8];
Bitboard ForwardBB[2][64];
Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64];
Bitboard PseudoAttacks[6][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];
int SquareDistance[64][64];
int SquareDistance[SQUARE_NB][SQUARE_NB];
namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL;
const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn_32 = 0x783A9B23;
CACHE_LINE_ALIGNMENT
int MS1BTable[256];
Square BSFTable[64];
Square BSFTable[SQUARE_NB];
Bitboard RTable[0x19000]; // Storage space for rook attacks
Bitboard BTable[0x1480]; // Storage space for bishop attacks
uint8_t BitCount8Bit[256];
typedef unsigned (Fn)(Square, Bitboard);
@@ -75,12 +74,10 @@ namespace {
FORCE_INLINE unsigned bsf_index(Bitboard b) {
if (Is64Bit)
return ((b & -b) * DeBruijn_64) >> 58;
// Use Matt Taylor's folding trick for 32 bit systems
// Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
b ^= (b - 1);
return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
return Is64Bit ? (b * DeBruijn_64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
}
}
@@ -161,9 +158,6 @@ void Bitboards::init() {
for (int i = 0; i < 64; i++)
BSFTable[bsf_index(1ULL << i)] = Square(i);
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
for (Square s = SQ_A1; s <= SQ_H8; s++)
SquareBB[s] = 1ULL << s;
@@ -326,7 +320,7 @@ namespace {
// until we find the one that passes the verification test.
do {
do magics[s] = pick_random(rk, booster);
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard));
+41 -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
@@ -33,36 +33,37 @@ void print(Bitboard b);
namespace Bitbases {
void init_kpk();
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
}
CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[64];
extern Bitboard RMagics[64];
extern Bitboard* RAttacks[64];
extern unsigned RShifts[64];
extern Bitboard RMasks[SQUARE_NB];
extern Bitboard RMagics[SQUARE_NB];
extern Bitboard* RAttacks[SQUARE_NB];
extern unsigned RShifts[SQUARE_NB];
extern Bitboard BMasks[64];
extern Bitboard BMagics[64];
extern Bitboard* BAttacks[64];
extern unsigned BShifts[64];
extern Bitboard BMasks[SQUARE_NB];
extern Bitboard BMagics[SQUARE_NB];
extern Bitboard* BAttacks[SQUARE_NB];
extern unsigned BShifts[SQUARE_NB];
extern Bitboard SquareBB[64];
extern Bitboard FileBB[8];
extern Bitboard RankBB[8];
extern Bitboard AdjacentFilesBB[8];
extern Bitboard ThisAndAdjacentFilesBB[8];
extern Bitboard InFrontBB[2][8];
extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64];
extern Bitboard DistanceRingsBB[64][8];
extern Bitboard ForwardBB[2][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64];
extern Bitboard PseudoAttacks[6][64];
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.
@@ -199,8 +200,7 @@ 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 Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
return BlackSquares & s ? BlackSquares : ~BlackSquares;
}
@@ -247,6 +247,21 @@ FORCE_INLINE Square msb(Bitboard b) {
return (Square) index;
}
# 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
@@ -265,7 +280,7 @@ FORCE_INLINE Square msb(Bitboard b) {
FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b);
*b &= ~(1ULL << s);
*b &= *b - 1;
return s;
}
+23 -30
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<>
@@ -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
}
+32 -35
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
@@ -38,7 +38,7 @@ 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 BookEntry {
struct Entry {
uint64_t key;
uint16_t move;
uint16_t count;
@@ -46,7 +46,15 @@ namespace {
};
// 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,
@@ -308,51 +316,40 @@ namespace {
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
};
}};
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms;
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
// polyglot_key() returns the PolyGlot hash key of the given position
Key polyglot_key(const Position& pos) {
// book_key() returns the PolyGlot hash key of the given position
uint64_t book_key(const Position& pos) {
uint64_t key = 0;
Key key = 0;
Bitboard b = pos.pieces();
while (b)
{
// In PolyGlotRandoms[] pieces are stored in the following sequence:
// BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
Square s = pop_lsb(&b);
Piece p = pos.piece_on(s);
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
key ^= ZobPiece[64 * pieceOfs + 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];
}
b = pos.can_castle(ALL_CASTLES);
while (b)
key ^= ZobCastle[pop_lsb(&b)];
key ^= PG.Zobrist.castle[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE)
key ^= ZobEnPassant[file_of(pos.ep_square())];
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
key ^= ZobTurn[0];
key ^= PG.Zobrist.turn;
return key;
}
} // namespace
PolyglotBook::PolyglotBook() {
for (int i = Time::now() % 10000; i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
@@ -370,7 +367,7 @@ template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
return *this;
}
template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) {
template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
}
@@ -400,13 +397,13 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE;
BookEntry e;
Entry e;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
uint64_t key = book_key(pos);
Key key = polyglot_key(pos);
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg);
seekg(find_first(key) * sizeof(Entry), ios_base::beg);
while (*this >> e, e.key == key && good())
{
@@ -416,7 +413,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
// 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 ( (sum && RKiss.rand<unsigned>() % sum < e.count)
if ( (sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
@@ -440,7 +437,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
// Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() & 0x3FFF))
if (move == (ml.move() ^ type_of(ml.move())))
return ml.move();
return MOVE_NONE;
@@ -451,12 +448,12 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
/// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input.
size_t PolyglotBook::find_first(uint64_t key) {
size_t PolyglotBook::find_first(Key key) {
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1;
BookEntry e;
size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
Entry e;
assert(low <= high);
@@ -466,7 +463,7 @@ size_t PolyglotBook::find_first(uint64_t key) {
assert(mid >= low && mid < high);
seekg(mid * sizeof(BookEntry), ios_base::beg);
seekg(mid * sizeof(Entry), ios_base::beg);
*this >> e;
if (key <= e.key)
+3 -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
@@ -36,9 +36,9 @@ private:
template<typename T> PolyglotBook& operator>>(T& n);
bool open(const char* fName);
size_t find_first(uint64_t key);
size_t find_first(Key key);
RKISS RKiss;
RKISS rkiss;
std::string fileName;
};
+87 -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
@@ -31,7 +31,7 @@ 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,
@@ -44,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,
@@ -95,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");
@@ -132,7 +134,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
// Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide
&& !pos.in_check()
&& !pos.checkers()
&& !MoveList<LEGAL>(pos).size()) {
return VALUE_DRAW;
}
@@ -169,12 +171,12 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
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);
@@ -198,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 = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
stm = ~pos.side_to_move();
us = ~pos.side_to_move();
}
if (file_of(wpsq) >= FILE_E)
@@ -222,7 +224,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
wpsq = mirror(wpsq);
}
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm))
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
@@ -326,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
@@ -439,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;
}
@@ -849,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
@@ -865,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 = ~wksq;
bksq = ~bksq;
wpsq = ~wpsq;
stm = ~stm;
us = ~us;
}
if (file_of(wpsq) >= FILE_E)
@@ -890,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 Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}
+4 -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
@@ -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,6 +58,7 @@ enum EndgameType {
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KNPKB, // KNP vs KB
KPKP // KP vs KP
};
@@ -111,7 +113,7 @@ class Endgames {
public:
Endgames();
~Endgames();
~Endgames();
template<typename T> T probe(Key key, T& eg)
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
+62 -64
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
@@ -36,13 +36,13 @@ namespace {
struct EvalInfo {
// Pointers to material and pawn hash table entries
MaterialEntry* mi;
PawnEntry* 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
@@ -50,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
@@ -88,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
@@ -114,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
@@ -134,7 +134,7 @@ 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
@@ -150,16 +150,18 @@ namespace {
#undef S
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(3, 20);
const Score QueenOn7thBonus = make_score(1, 8);
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(3, 48);
const Score QueenOnPawnBonus = make_score(1, 40);
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, 21);
@@ -169,6 +171,9 @@ namespace {
// 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.
@@ -221,11 +226,11 @@ 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 {
@@ -267,8 +272,6 @@ namespace {
namespace Eval {
Color RootColor;
/// 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.
@@ -289,14 +292,6 @@ namespace Eval {
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
// King safety is asymmetrical. Our king danger level is weighted by
// "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness".
// 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;
const int MaxSlope = 30;
const int Peak = 1280;
@@ -319,7 +314,7 @@ namespace Eval {
Value margin;
std::string totals;
RootColor = pos.side_to_move();
Search::RootColor = pos.side_to_move();
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
@@ -363,11 +358,12 @@ namespace {
template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) {
assert(!pos.in_check());
assert(!pos.checkers());
EvalInfo ei;
Value margins[2];
Value margins[COLOR_NB];
Score score, mobilityWhite, mobilityBlack;
Thread* th = pos.this_thread();
// margins[] store the uncertainty estimation of position's evaluation
// that typically is used by the search for pruning decisions.
@@ -379,7 +375,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
// Probe the material hash table
ei.mi = pos.this_thread()->materialTable.probe(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
@@ -391,7 +387,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// Probe the pawn hash table
ei.pi = pos.this_thread()->pawnTable.probe(pos);
ei.pi = Pawns::probe(pos, th->pawnsTable);
score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards
@@ -496,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg)
&& pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN];
@@ -577,23 +573,22 @@ Value do_evaluate(const Position& pos, Value& margin) {
mobility += MobilityBonus[Piece][mob];
// Add a bonus if a slider is pinning an enemy piece
if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN)
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s))
{
b = BetweenBB[s][pos.king_square(Them)] & pos.pieces();
assert(b);
if (!more_than_one(b) && (b & pos.pieces(Them)))
score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))];
}
// 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 (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(Them, PAWN) & attack_span_mask(Us, s)))
@@ -696,16 +691,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them)
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
& ~ei.attackedBy[Them][0];
& ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors)
score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2
: UndefendedMinorPenalty;
score += UndefendedMinorPenalty;
// Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][0];
& ei.attackedBy[Us][ALL_PIECES];
if (!weakEnemies)
return score;
@@ -744,9 +738,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
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;
}
@@ -772,7 +766,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
{
// 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]);
@@ -820,7 +814,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
}
// 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;
@@ -853,8 +847,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// 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 == Eval::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]);
score -= KingDangerTable[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]);
}
if (Trace)
@@ -907,7 +901,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
if (pos.is_empty(blockSq))
{
squaresToQueen = forward_bb(Us, s);
defendedSquares = squaresToQueen & ei.attackedBy[Us][0];
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
@@ -916,7 +910,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
&& (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.
@@ -993,14 +987,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
// 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(~c), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
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(!pos.checkers());
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition
@@ -1140,14 +1134,18 @@ Value do_evaluate(const Position& pos, Value& margin) {
Bitboard safe = SpaceMask[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(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));
}
+1 -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
@@ -26,8 +26,6 @@ class Position;
namespace Eval {
extern Color RootColor;
extern void init();
extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace(const Position& pos);
-72
View File
@@ -1,72 +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 <algorithm>
#include <cstring>
#include "types.h"
/// 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)
+1 -1
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
+69 -53
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,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <algorithm> // For std::min
#include <cassert>
#include <cstring>
@@ -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.
@@ -81,18 +95,54 @@ namespace {
&& 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 {
/// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry
/// 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.
MaterialEntry* MaterialTable::probe(const Position& pos) {
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Key key = pos.material_key();
MaterialEntry* e = entries[key];
Entry* e = entries[key];
// If e->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
@@ -100,10 +150,10 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
if (e->key == key)
return e;
memset(e, 0, sizeof(MaterialEntry));
memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = MaterialTable::game_phase(pos);
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
@@ -215,7 +265,7 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
// 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),
@@ -226,47 +276,11 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
}
/// MaterialTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int MaterialTable::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;
}
/// MaterialTable::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 MaterialEntry.
Phase MaterialTable::game_phase(const Position& pos) {
Phase game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
@@ -274,3 +288,5 @@ Phase MaterialTable::game_phase(const Position& pos) {
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
} // namespace Material
+21 -64
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,96 +25,53 @@
#include "position.h"
#include "types.h"
const int MaterialTableSize = 8192;
namespace Material {
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialEntry 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 MaterialEntry {
struct Entry {
friend struct MaterialTable;
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 MaterialTable class represents a material hash table. The most important
/// method is probe(), which returns a pointer to a MaterialEntry object.
Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
Phase game_phase(const Position& pos);
struct MaterialTable {
MaterialEntry* probe(const Position& pos);
static Phase game_phase(const Position& pos);
template<Color Us> static int imbalance(const int pieceCount[][8]);
HashTable<MaterialEntry, MaterialTableSize> entries;
Endgames endgames;
};
/// MaterialEntry::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 MaterialEntry::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 MaterialEntry::evaluate(const Position& pos) const {
return (*evaluationFunction)(pos);
}
inline Score MaterialEntry::material_value() const {
return make_score(value, value);
}
inline int MaterialEntry::space_weight() const {
return spaceWeight;
}
inline Phase MaterialEntry::game_phase() const {
return gamePhase;
}
inline bool MaterialEntry::specialized_eval_exists() const {
return evaluationFunction != NULL;
}
#endif // !defined(MATERIAL_H_INCLUDED)
+17 -14
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
@@ -31,16 +31,16 @@
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 = "";
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) {
@@ -57,8 +57,8 @@ const string engine_info(bool to_uci) {
{
date >> month >> day >> year;
s << Tag << setfill('0') << " " << year.substr(2)
<< setw(2) << (1 + months.find(month) / 4) << setw(2) << day;
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
}
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ")
@@ -227,18 +227,21 @@ void prefetch(char*) {}
#else
# include <xmmintrin.h>
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
+1 -1
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 -39
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
@@ -44,7 +44,7 @@ namespace {
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~us);
assert(!pos.in_check());
assert(!pos.checkers());
const int K = Chess960 ? kto > kfrom ? -1 : 1
: Side == KING_SIDE ? -1 : 1;
@@ -249,41 +249,38 @@ namespace {
}
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
Color us, Bitboard target) {
Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b);
return mlist;
}
template<GenType Type> FORCE_INLINE
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us,
Bitboard target, const CheckInfo* ci = NULL) {
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, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<BISHOP, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<ROOK, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Type == QUIET_CHECKS>(pos, mlist, us, 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)
mlist = generate_king_moves(pos, mlist, us, target);
{
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, Type == QUIET_CHECKS, true>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, true>(pos, mlist, us);
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, Type == QUIET_CHECKS, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS, false>(pos, mlist, us);
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us);
}
}
@@ -307,21 +304,15 @@ template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.in_check());
assert(!pos.checkers());
Color us = pos.side_to_move();
Bitboard target;
if (Type == CAPTURES)
target = pos.pieces(~us);
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
: Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
else if (Type == QUIETS)
target = ~pos.pieces();
else if (Type == NON_EVASIONS)
target = ~pos.pieces(us);
return generate_all_moves<Type>(pos, mlist, us, target);
return generate_all<Type>(pos, mlist, us, target);
}
// Explicit template instantiations
@@ -335,9 +326,8 @@ template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
template<>
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
assert(!pos.in_check());
assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
@@ -357,7 +347,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
SERIALIZE(b);
}
return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci);
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci);
}
@@ -366,7 +356,7 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
template<>
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
assert(pos.in_check());
assert(pos.checkers());
Square from, checksq;
int checkersCnt = 0;
@@ -419,7 +409,7 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
// Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
return generate_all_moves<EVASIONS>(pos, mlist, us, target);
return generate_all<EVASIONS>(pos, mlist, us, target);
}
@@ -432,7 +422,7 @@ MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move());
end = pos.in_check() ? generate<EVASIONS>(pos, mlist)
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)
+5 -1
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
@@ -46,6 +46,10 @@ struct MoveList {
bool end() const { return cur == last; }
Move move() const { return cur->move; }
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];
+58 -45
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,10 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "movegen.h"
#include "movepick.h"
#include "thread.h"
@@ -37,6 +35,20 @@ namespace {
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& ms) { return ms.score > 0; }
@@ -59,7 +71,7 @@ namespace {
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Search::Stack* s, Value beta) : pos(p), H(h), depth(d) {
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) {
assert(d > DEPTH_ZERO);
@@ -68,7 +80,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
endBadCaptures = moves + MAX_MOVES - 1;
ss = s;
if (p.in_check())
if (p.checkers())
phase = EVASION;
else
@@ -79,12 +91,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
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 - PawnValueMg && d < 3 * ONE_PLY)
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;
else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->staticEval;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
@@ -92,11 +104,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Square sq) : pos(p), H(h), cur(moves), end(moves) {
Square sq) : pos(p), Hist(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO);
if (p.in_check())
if (p.checkers())
phase = EVASION;
else if (d > DEPTH_QS_NO_CHECKS)
@@ -124,14 +136,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), H(h), cur(moves), end(moves) {
: pos(p), Hist(h), cur(moves), end(moves) {
assert(!pos.in_check());
assert(!pos.checkers());
phase = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[Mg][pt];
captureThreshold = PieceValue[MG][pt];
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
@@ -141,12 +153,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType
}
/// 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
@@ -165,51 +175,54 @@ void MovePicker::score_captures() {
for (MoveStack* it = moves; it != end; ++it)
{
m = it->move;
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m));
if (type_of(m) == PROMOTION)
it->score += PieceValue[Mg][promotion_type(m)];
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;
for (MoveStack* it = moves; it != end; ++it)
{
m = it->move;
it->score = H.value(pos.piece_moved(m), to_sq(m));
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
}
}
void MovePicker::score_evasions() {
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;
if (end < moves + 2)
return;
for (MoveStack* it = moves; it != end; ++it)
{
m = it->move;
if ((seeScore = pos.see_sign(m)) < 0)
it->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))
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::MaxValue;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::Max;
else
it->score = H.value(pos.piece_moved(m), to_sq(m));
it->score = Hist[pos.piece_moved(m)][to_sq(m)];
}
}
/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves,
/// when there are no more moves to try for the current phase.
/// 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() {
@@ -219,7 +232,7 @@ void MovePicker::generate_next() {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves);
score_captures();
score<CAPTURES>();
return;
case KILLERS_S1:
@@ -229,16 +242,16 @@ void MovePicker::generate_next() {
case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves);
score_noncaptures();
score<QUIETS>();
end = std::partition(cur, end, has_positive_score);
sort<MoveStack>(cur, end);
insertion_sort(cur, end);
return;
case QUIETS_2_S1:
cur = end;
end = endQuiets;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(cur, end);
insertion_sort(cur, end);
return;
case BAD_CAPTURES_S1:
@@ -249,7 +262,8 @@ void MovePicker::generate_next() {
case EVASIONS_S2:
end = generate<EVASIONS>(pos, moves);
score_evasions();
if (end > moves + 1)
score<EVASIONS>();
return;
case QUIET_CHECKS_S3:
@@ -268,11 +282,10 @@ void MovePicker::generate_next() {
}
/// 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.
/// 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>() {
@@ -359,6 +372,6 @@ Move MovePicker::next_move<false>() {
/// 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 should be lock protected by the caller.
/// safe so must be lock protected by the caller.
template<>
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); }
Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }
+40 -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
@@ -20,12 +20,48 @@
#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"
/// 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,
@@ -44,13 +80,11 @@ public:
template<bool SpNode> Move next_move();
private:
void score_captures();
void score_noncaptures();
void score_evasions();
template<GenType> void score();
void generate_next();
const Position& pos;
const History& H;
const History& Hist;
Search::Stack* ss;
Depth depth;
Move ttMove;
+21 -29
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,7 @@
#include <cassert>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include "movegen.h"
#include "notation.h"
@@ -28,7 +28,7 @@
using namespace std;
static const char* PieceToChar = " PNBRQK pnbrqk";
static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" };
/// score_to_uci() converts a value to a string suitable for use with the UCI
@@ -75,7 +75,7 @@ const string move_to_uci(Move m, bool chess960) {
string move = square_to_string(from) + square_to_string(to);
if (type_of(m) == PROMOTION)
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case
move += PieceToChar[BLACK][promotion_type(m)]; // Lower case
return move;
}
@@ -108,10 +108,9 @@ const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NULL)
return "(null)";
assert(pos.move_is_legal(m));
assert(MoveList<LEGAL>(pos).contains(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
Bitboard others, b;
string san;
Color us = pos.side_to_move();
Square from = from_sq(m);
@@ -125,33 +124,25 @@ const string move_to_san(Position& pos, Move m) {
{
if (pt != PAWN)
{
san = PieceToChar[pt]; // Upper case
san = PieceToChar[WHITE][pt]; // Upper case
// Disambiguation if we have more then one piece with destination 'to'
// note that for pawns is not needed because starting file is explicit.
ambiguousMove = ambiguousFile = ambiguousRank = false;
// 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;
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (attackers)
while (b)
{
Square sq = pop_lsb(&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;
ambiguousFile |= file_of(sq) == file_of(from);
ambiguousRank |= rank_of(sq) == rank_of(from);
ambiguousMove = true;
Move move = make_move(pop_lsb(&b), to);
if (!pos.pl_move_is_legal(move, pos.pinned_pieces()))
others ^= from_sq(move);
}
if (ambiguousMove)
if (others)
{
if (!ambiguousFile)
if (!(others & file_bb(from)))
san += file_to_char(file_of(from));
else if (!ambiguousRank)
else if (!(others & rank_bb(from)))
san += rank_to_char(rank_of(from));
else
@@ -167,7 +158,7 @@ const string move_to_san(Position& pos, Move m) {
san += square_to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[promotion_type(m)];
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
}
if (pos.move_gives_check(m, CheckInfo(pos)))
@@ -226,7 +217,7 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
const int64_t K = 1000;
const int64_t M = 1000000;
StateInfo state[MAX_PLY_PLUS_2], *st = state;
std::stack<StateInfo> st;
Move* m = pv;
string san, padding;
size_t length;
@@ -261,7 +252,8 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]
s << san << ' ';
length += san.length() + 1;
pos.do_move(*m++, *st++);
st.push(StateInfo());
pos.do_move(*m++, st.top());
}
while (m != pv)
+1 -1
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
+127 -122
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
@@ -30,34 +30,34 @@ namespace {
#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)
};
@@ -65,12 +65,12 @@ namespace {
const Score PawnStructureWeight = S(233, 201);
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank]
const Value ShelterWeakness[2][8] =
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) } };
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank]
const Value StormDanger[2][8] =
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) } };
@@ -80,18 +80,122 @@ namespace {
#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 {
/// PawnTable::probe() takes a position object as input, computes a PawnEntry
/// 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.
/// 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.
PawnEntry* PawnTable::probe(const Position& pos) {
Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key();
PawnEntry* e = entries[key];
Entry* e = entries[key];
// 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
@@ -118,112 +222,11 @@ PawnEntry* PawnTable::probe(const Position& pos) {
}
/// PawnTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us>
Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* 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)];
}
return value;
}
/// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file
/// 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>
Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -234,7 +237,7 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
Rank rkUs, rkThem;
File kf = file_of(ksq);
kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf;
kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;
for (int f = kf - 1; f <= kf + 1; f++)
{
@@ -253,11 +256,11 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
}
/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is
/// 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 PawnEntry::update_safety(const Position& pos, Square ksq) {
Score Entry::update_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq;
castleRights[Us] = pos.can_castle(Us);
@@ -283,5 +286,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
}
// Explicit template instantiation
template Score PawnEntry::update_safety<WHITE>(const Position& pos, Square ksq);
template Score PawnEntry::update_safety<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
+30 -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
@@ -24,31 +24,31 @@
#include "position.h"
#include "types.h"
const int PawnTableSize = 16384;
namespace Pawns {
/// PawnEntry 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 probe method in a PawnTable object)
/// returns a pointer to a PawnEntry 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 PawnEntry {
struct Entry {
friend struct PawnTable;
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_safety(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 update_safety(const Position& pos, Square ksq);
@@ -56,60 +56,21 @@ private:
Value shelter_storm(const Position& pos, Square ksq);
Key key;
Bitboard passedPawns[2];
Bitboard pawnAttacks[2];
Square kingSquares[2];
int minKPdistance[2];
int castleRights[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 kingSafety[2];
int halfOpenFiles[COLOR_NB];
Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB];
};
typedef HashTable<Entry, 16384> Table;
/// The PawnTable class represents a pawn hash table. The most important
/// method is probe, which returns a pointer to a PawnEntry object.
Entry* probe(const Position& pos, Table& entries);
struct PawnTable {
PawnEntry* probe(const Position& pos);
template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e);
HashTable<PawnEntry, PawnTableSize> entries;
};
inline Score PawnEntry::pawns_value() const {
return value;
}
inline Bitboard PawnEntry::pawn_attacks(Color c) const {
return pawnAttacks[c];
}
inline Bitboard PawnEntry::passed_pawns(Color c) const {
return passedPawns[c];
}
inline int PawnEntry::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f));
}
inline int PawnEntry::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1);
}
inline int PawnEntry::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
}
template<Color Us>
inline Score PawnEntry::king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<Us>(pos, ksq);
}
#endif // !defined(PAWNS_H_INCLUDED)
+6 -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
@@ -40,6 +40,7 @@ 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
@@ -92,6 +93,9 @@ 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))
@@ -101,7 +105,7 @@ typedef HANDLE NativeHandle;
# 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,NULL), x != NULL)
# 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
+222 -326
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,6 +19,7 @@
#include <cassert>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <algorithm>
@@ -40,16 +41,16 @@ static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT
Score pieceSquareTable[16][64]; // [piece][square]
Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType]
Score pieceSquareTable[PIECE_NB][SQUARE_NB];
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
namespace Zobrist {
Key psq[2][8][64]; // [color][pieceType][square / piece count]
Key enpassant[8]; // [file]
Key castle[16]; // [castleRight]
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Key enpassant[FILE_NB];
Key castle[CASTLE_RIGHT_NB];
Key side;
Key exclusion;
@@ -86,10 +87,10 @@ void init() {
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[Mg][make_piece(BLACK, pt)] = PieceValue[Mg][pt];
PieceValue[Eg][make_piece(BLACK, pt)] = PieceValue[Eg][pt];
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[Mg][pt], PieceValue[Eg][pt]);
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
@@ -172,11 +173,11 @@ Position& Position::operator=(const Position& pos) {
}
/// Position::from_fen() initializes the position object with the given FEN
/// string. This function is not very robust - make sure that input FENs are
/// correct (this is assumed to be the responsibility of the GUI).
/// Position::set() initializes the position object with the given FEN string.
/// This function is not very robust - make sure that input FENs are correct,
/// this is assumed to be the responsibility of the GUI.
void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
void Position::set(const string& fenStr, bool isChess960, Thread* th) {
/*
A FEN string defines a particular position using only the ASCII character set.
@@ -214,13 +215,13 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
char col, row, token;
size_t p;
Square sq = SQ_A8;
std::istringstream fen(fenStr);
std::istringstream ss(fenStr);
clear();
fen >> std::noskipws;
ss >> std::noskipws;
// 1. Piece placement
while ((fen >> token) && !isspace(token))
while ((ss >> token) && !isspace(token))
{
if (isdigit(token))
sq += Square(token - '0'); // Advance the given number of files
@@ -236,16 +237,16 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
}
// 2. Active color
fen >> token;
ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK);
fen >> token;
ss >> token;
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
// Shredder-FEN that uses the letters of the columns on which the rooks began
// the game instead of KQkq and also X-FEN standard that, in case of Chess960,
// if an inner rook is associated with the castling right, the castling tag is
// replaced by the file letter of the involved rook, as for the Shredder-FEN.
while ((fen >> token) && !isspace(token))
while ((ss >> token) && !isspace(token))
{
Square rsq;
Color c = islower(token) ? BLACK : WHITE;
@@ -268,8 +269,8 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
}
// 4. En passant square. Ignore if no pawn capture is possible
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
&& ((fen >> row) && (row == '3' || row == '6')))
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
&& ((ss >> row) && (row == '3' || row == '6')))
{
st->epSquare = File(col - 'a') | Rank(row - '1');
@@ -278,11 +279,11 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
}
// 5-6. Halfmove clock and fullmove number
fen >> std::skipws >> st->rule50 >> startPosPly;
ss >> std::skipws >> st->rule50 >> gamePly;
// Convert from fullmove starting from 1 to ply starting from 0,
// handle also common incorrect FEN with fullmove = 0.
startPosPly = std::max(2 * (startPosPly - 1), 0) + int(sideToMove == BLACK);
gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
st->key = compute_key();
st->pawnKey = compute_pawn_key();
@@ -325,71 +326,64 @@ void Position::set_castle_right(Color c, Square rfrom) {
}
/// Position::to_fen() returns a FEN representation of the position. In case
/// Position::fen() returns a FEN representation of the position. In case
/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function.
const string Position::to_fen() const {
const string Position::fen() const {
std::ostringstream fen;
Square sq;
int emptyCnt;
std::ostringstream ss;
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{
emptyCnt = 0;
for (File file = FILE_A; file <= FILE_H; file++)
{
sq = file | rank;
Square sq = file | rank;
if (is_empty(sq))
emptyCnt++;
else
{
if (emptyCnt > 0)
{
fen << emptyCnt;
emptyCnt = 0;
}
fen << PieceToChar[piece_on(sq)];
int emptyCnt = 1;
for ( ; file < FILE_H && is_empty(sq++); file++)
emptyCnt++;
ss << emptyCnt;
}
else
ss << PieceToChar[piece_on(sq)];
}
if (emptyCnt > 0)
fen << emptyCnt;
if (rank > RANK_1)
fen << '/';
ss << '/';
}
fen << (sideToMove == WHITE ? " w " : " b ");
ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO))
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE))))) : 'K');
ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K');
if (can_castle(WHITE_OOO))
fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE))))) : 'Q');
ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q');
if (can_castle(BLACK_OO))
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE))) : 'k');
ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k');
if (can_castle(BLACK_OOO))
fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE))) : 'q');
ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q');
if (st->castleRights == CASTLES_NONE)
fen << '-';
ss << '-';
fen << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
<< st->rule50 << " " << 1 + (startPosPly - int(sideToMove == BLACK)) / 2;
ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
<< st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
return fen.str();
return ss.str();
}
/// Position::print() prints an ASCII representation of the position to
/// the standard output. If a move is given then also the san is printed.
/// Position::pretty() returns an ASCII representation of the position to be
/// printed to the standard output together with the move's san notation.
void Position::print(Move move) const {
const string Position::pretty(Move move) const {
const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
@@ -397,19 +391,27 @@ void Position::print(Move move) const {
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
sync_cout;
std::ostringstream ss;
if (move)
{
Position p(*this);
cout << "\nMove is: " << (sideToMove == BLACK ? ".." : "") << move_to_san(p, move);
}
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), move);
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl;
ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
for (Bitboard b = checkers(); b; )
ss << square_to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " ";
return ss.str();
}
@@ -475,37 +477,6 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
}
/// Position::move_attacks_square() tests whether a move from the current
/// position attacks a given square.
bool Position::move_attacks_square(Move m, Square s) const {
assert(is_ok(m));
assert(is_ok(s));
Bitboard occ, xray;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_moved(m);
assert(!is_empty(from));
// Update occupancy as if the piece is moving
occ = pieces() ^ from ^ to;
// The piece moved in 'to' attacks the square 's' ?
if (attacks_from(piece, to, occ) & s)
return true;
// Scan for possible X-ray attackers behind the moved piece
xray = (attacks_bb< ROOK>(s, occ) & pieces(color_of(piece), QUEEN, ROOK))
| (attacks_bb<BISHOP>(s, occ) & pieces(color_of(piece), QUEEN, BISHOP));
// Verify attackers are triggered by our move and not already existing
return xray && (xray ^ (xray & attacks_from<QUEEN>(s)));
}
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
@@ -553,20 +524,6 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
}
/// Position::move_is_legal() takes a random move and tests whether the move
/// is legal. This version is not very fast and should be used only in non
/// time-critical paths.
bool Position::move_is_legal(const Move m) const {
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
if (ml.move() == m)
return true;
return false;
}
/// Position::is_pseudo_legal() takes a random move and tests whether the move
/// is pseudo legal. It is used to validate moves from TT that can be corrupted
/// due to SMP concurrent access or hash position key aliasing.
@@ -574,14 +531,13 @@ bool Position::move_is_legal(const Move m) const {
bool Position::is_pseudo_legal(const Move m) const {
Color us = sideToMove;
Color them = ~sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = piece_moved(m);
// Use a slower but simpler function for uncommon cases
if (type_of(m) != NORMAL)
return move_is_legal(m);
return MoveList<LEGAL>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - 2 != NO_PIECE_TYPE)
@@ -593,7 +549,7 @@ bool Position::is_pseudo_legal(const Move m) const {
return false;
// The destination square cannot be occupied by a friendly piece
if (color_of(piece_on(to)) == us)
if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us)
return false;
// Handle the special case of a pawn move
@@ -619,7 +575,7 @@ bool Position::is_pseudo_legal(const Move m) const {
case DELTA_SE:
// Capture. The destination square must be occupied by an enemy
// piece (en passant captures was handled earlier).
if (color_of(piece_on(to)) != them)
if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us)
return false;
// From and to files must be one file apart, avoids a7h5
@@ -664,18 +620,16 @@ bool Position::is_pseudo_legal(const Move m) const {
// Evasions generator already takes care to avoid some kind of illegal moves
// and pl_move_is_legal() relies on this. So we have to take care that the
// same kind of moves are filtered out here.
if (in_check())
if (checkers())
{
if (type_of(pc) != KING)
{
Bitboard b = checkers();
Square checksq = pop_lsb(&b);
if (b) // double check ? In this case a king move is required
// Double check? In this case a king move is required
if (more_than_one(checkers()))
return false;
// Our move must be a blocking evasion or a capture of the checking piece
if (!((between_bb(checksq, king_square(us)) | checkers()) & to))
if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to))
return false;
}
// In case of king moves under check we have to remove king so to catch
@@ -720,15 +674,16 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
Color us = sideToMove;
Square ksq = king_square(~us);
// Promotion with check ?
if (type_of(m) == PROMOTION)
switch (type_of(m))
{
case PROMOTION:
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
// En passant capture with check ? We have already handled the case
// of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through
// the captured pawn.
if (type_of(m) == ENPASSANT)
case ENPASSANT:
{
Square capsq = file_of(to) | rank_of(from);
Bitboard b = (pieces() ^ from ^ capsq) | to;
@@ -736,9 +691,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
| (attacks_bb<BISHOP>(ksq, b) & pieces(us, QUEEN, BISHOP));
}
// Castling with check ?
if (type_of(m) == CASTLE)
case CASTLE:
{
Square kfrom = from;
Square rfrom = to; // 'King captures the rook' notation
@@ -748,8 +701,10 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return attacks_bb<ROOK>(rto, b) & ksq;
}
return false;
default:
assert(false);
return false;
}
}
@@ -772,9 +727,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Key k = st->key;
// Copy some fields of old state to our new StateInfo object except the ones
// which are recalculated from scratch anyway, then switch our state pointer
// to point to the new, ready to be updated, state.
memcpy(&newSt, st, sizeof(ReducedStateInfo));
// which are going to be recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state.
memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st;
st = &newSt;
@@ -782,18 +737,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update side to move
k ^= Zobrist::side;
// Increment the 50 moves rule draw counter. Resetting it to zero in the
// case of a capture or a pawn move is taken care of later.
// Increment ply counters.In particular rule50 will be later reset it to zero
// in case of a capture or a pawn move.
gamePly++;
st->rule50++;
st->pliesFromNull++;
if (type_of(m) == CASTLE)
{
st->key = k;
do_castle_move<true>(m);
return;
}
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
@@ -803,9 +752,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us);
assert(color_of(piece_on(to)) != us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING);
if (type_of(m) == CASTLE)
{
assert(piece == make_piece(us, KING));
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE;
do_castle(from, to, rfrom, rto);
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto);
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
}
if (capture)
{
Square capsq = to;
@@ -830,7 +795,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
}
else
st->npMaterial[them] -= PieceValue[Mg][capture];
st->npMaterial[them] -= PieceValue[MG][capture];
// Remove the captured piece
byTypeBB[ALL_PIECES] ^= capsq;
@@ -840,7 +805,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Update piece list, move the last piece at index[capsq] position and
// shrink the list.
//
// WARNING: This is a not revresible operation. When we will reinsert the
// WARNING: This is a not reversible operation. When we will reinsert the
// captured piece in undo_move() we will put it at the end of the list and
// not in its original place, it means index[] and pieceList[] are not
// guaranteed to be invariant to a do_move() + undo_move() sequence.
@@ -849,9 +814,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
pieceList[them][capture][index[lastSquare]] = lastSquare;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update hash keys
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq];
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
@@ -878,22 +844,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->castleRights &= ~cr;
}
// Prefetch TT access as soon as we know key is updated
// Prefetch TT access as soon as we know the new hash key
prefetch((char*)TT.first_entry(k));
// Move the piece
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
// Move the piece. The tricky Chess960 castle is handled earlier
if (type_of(m) != CASTLE)
{
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[to] = board[from];
board[from] = NO_PIECE;
board[from] = NO_PIECE;
board[to] = piece;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[to] = index[from];
pieceList[us][pt][index[to]] = to;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[to] = index[from];
pieceList[us][pt][index[to]] = to;
}
// If the moving piece is a pawn do some special extra work
if (pt == PAWN)
@@ -938,20 +907,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
- pieceSquareTable[make_piece(us, PAWN)][to];
// Update material
st->npMaterial[us] += PieceValue[Mg][promotion];
st->npMaterial[us] += PieceValue[MG][promotion];
}
// Update pawn hash key
// Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
prefetch((char*)thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
st->rule50 = 0;
}
// Prefetch pawn and material hash tables
prefetch((char*)thisThread->pawnTable.entries[st->pawnKey]);
prefetch((char*)thisThread->materialTable.entries[st->materialKey]);
// Update incremental scores
st->psqScore += psq_delta(piece, from, to);
@@ -1001,22 +967,14 @@ void Position::undo_move(Move m) {
sideToMove = ~sideToMove;
if (type_of(m) == CASTLE)
{
do_castle_move<false>(m);
return;
}
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_on(to);
PieceType pt = type_of(piece);
PieceType pt = type_of(piece_on(to));
PieceType capture = st->capturedType;
assert(is_empty(from));
assert(color_of(piece) == us);
assert(is_empty(from) || type_of(m) == CASTLE);
assert(capture != KING);
if (type_of(m) == PROMOTION)
@@ -1044,19 +1002,32 @@ void Position::undo_move(Move m) {
pt = PAWN;
}
// Put the piece back at the source square
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
if (type_of(m) == CASTLE)
{
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE;
pt = KING;
do_castle(to, from, rto, rfrom);
}
else
{
// Put the piece back at the source square
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[from] = board[to];
board[to] = NO_PIECE;
board[to] = NO_PIECE;
board[from] = make_piece(us, pt);
// Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[from] = index[to];
pieceList[us][pt][index[from]] = from;
// Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[from] = index[to];
pieceList[us][pt][index[from]] = from;
}
if (capture)
{
@@ -1086,49 +1057,18 @@ void Position::undo_move(Move m) {
// Finally point our state pointer back to the previous state
st = st->previous;
gamePly--;
assert(pos_is_ok());
}
/// Position::do_castle_move() is a private method used to do/undo a castling
/// move. Note that castling moves are encoded as "king captures friendly rook"
/// moves, for instance white short castling in a non-Chess960 game is encoded
/// as e1h1.
template<bool Do>
void Position::do_castle_move(Move m) {
/// Position::do_castle() is a helper used to do/undo a castling move. This
/// is a bit tricky, especially in Chess960.
assert(is_ok(m));
assert(type_of(m) == CASTLE);
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
Color us = sideToMove;
Square kBefore = from_sq(m);
Square rBefore = to_sq(m);
// Find after-castle squares for king and rook
if (rBefore > kBefore) // O-O
{
kAfter = relative_square(us, SQ_G1);
rAfter = relative_square(us, SQ_F1);
}
else // O-O-O
{
kAfter = relative_square(us, SQ_C1);
rAfter = relative_square(us, SQ_D1);
}
kfrom = Do ? kBefore : kAfter;
rfrom = Do ? rBefore : rAfter;
kto = Do ? kAfter : kBefore;
rto = Do ? rAfter : rBefore;
assert(piece_on(kfrom) == make_piece(us, KING));
assert(piece_on(rfrom) == make_piece(us, ROOK));
// Move the pieces, with some care; in chess960 could be kto == rfrom
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto];
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto];
byTypeBB[KING] ^= k_from_to_bb;
@@ -1136,105 +1076,63 @@ void Position::do_castle_move(Move m) {
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb;
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
// Update board
Piece king = make_piece(us, KING);
Piece rook = make_piece(us, ROOK);
// Could be from == to, so first set NO_PIECE then KING and ROOK
board[kfrom] = board[rfrom] = NO_PIECE;
board[kto] = king;
board[rto] = rook;
board[kto] = make_piece(us, KING);
board[rto] = make_piece(us, ROOK);
// Update piece lists
pieceList[us][KING][index[kfrom]] = kto;
pieceList[us][ROOK][index[rfrom]] = rto;
int tmp = index[rfrom]; // In Chess960 could be kto == rfrom
index[kto] = index[kfrom];
index[rto] = tmp;
if (Do)
{
// Reset capture field
st->capturedType = NO_PIECE_TYPE;
// Update incremental scores
st->psqScore += psq_delta(king, kfrom, kto);
st->psqScore += psq_delta(rook, rfrom, rto);
// Update hash key
st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
// Clear en passant square
if (st->epSquare != SQ_NONE)
{
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->epSquare = SQ_NONE;
}
// Update castling rights
st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
st->castleRights &= ~castleRightsMask[kfrom];
// Update checkers BB
st->checkersBB = attackers_to(king_square(~us)) & pieces(us);
sideToMove = ~sideToMove;
}
else
// Undo: point our state pointer back to the previous state
st = st->previous;
assert(pos_is_ok());
// Could be kfrom == rto, so use a 'tmp' variable
int tmp = index[kfrom];
index[rto] = index[rfrom];
index[kto] = tmp;
pieceList[us][KING][index[kto]] = kto;
pieceList[us][ROOK][index[rto]] = rto;
}
/// Position::do_null_move() is used to do/undo a "null move": It flips the side
/// to move and updates the hash key without executing any move on the board.
template<bool Do>
void Position::do_null_move(StateInfo& backupSt) {
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
/// the side to move without executing any move on the board.
assert(!in_check());
void Position::do_null_move(StateInfo& newSt) {
// Back up the information necessary to undo the null move to the supplied
// StateInfo object. Note that differently from normal case here backupSt
// is actually used as a backup storage not as the new state. This reduces
// the number of fields to be copied.
StateInfo* src = Do ? st : &backupSt;
StateInfo* dst = Do ? &backupSt : st;
assert(!checkers());
dst->key = src->key;
dst->epSquare = src->epSquare;
dst->psqScore = src->psqScore;
dst->rule50 = src->rule50;
dst->pliesFromNull = src->pliesFromNull;
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st;
st = &newSt;
if (st->epSquare != SQ_NONE)
{
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->epSquare = SQ_NONE;
}
st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key));
st->rule50++;
st->pliesFromNull = 0;
sideToMove = ~sideToMove;
if (Do)
{
if (st->epSquare != SQ_NONE)
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key));
st->epSquare = SQ_NONE;
st->rule50++;
st->pliesFromNull = 0;
}
assert(pos_is_ok());
}
// Explicit template instantiations
template void Position::do_null_move<false>(StateInfo& backupSt);
template void Position::do_null_move<true>(StateInfo& backupSt);
void Position::undo_null_move() {
assert(!checkers());
st = st->previous;
sideToMove = ~sideToMove;
}
/// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move. There are three versions of
/// this function: One which takes a destination square as input, one takes a
/// move, and one which takes a 'from' and a 'to' square. The function does
/// not yet understand promotions captures.
/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes
/// tempi into account. If the side who initiated the capturing sequence does the
/// last capture, he loses a tempo and if the result is below 'asymmThreshold'
/// the capturing sequence is considered bad.
int Position::see_sign(Move m) const {
@@ -1243,13 +1141,13 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0.
if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)])
if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)])
return 1;
return see(m);
}
int Position::see(Move m) const {
int Position::see(Move m, int asymmThreshold) const {
Square from, to;
Bitboard occupied, attackers, stmAttackers;
@@ -1290,7 +1188,7 @@ int Position::see(Move m) const {
stm = ~color_of(piece_on(from));
stmAttackers = attackers & pieces(stm);
if (!stmAttackers)
return PieceValue[Mg][captured];
return PieceValue[MG][captured];
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
@@ -1298,14 +1196,14 @@ int Position::see(Move m) const {
// destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece.
swapList[0] = PieceValue[Mg][captured];
swapList[0] = PieceValue[MG][captured];
captured = type_of(piece_on(from));
do {
assert(slIndex < 32);
// Add the new entry to the swap list
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured];
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++;
// Locate and remove from 'occupied' the next least valuable attacker
@@ -1326,6 +1224,15 @@ int Position::see(Move m) const {
} while (stmAttackers);
// If we are doing asymmetric SEE evaluation and the same side does the first
// and the last capture, he loses a tempo and gain must be at least worth
// 'asymmThreshold', otherwise we replace the score with a very low value,
// before negamaxing.
if (asymmThreshold)
for (int i = 0; i < slIndex; i += 2)
if (swapList[i] < asymmThreshold)
swapList[i] = - QueenValueMg * 16;
// Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move.
while (--slIndex)
@@ -1347,9 +1254,6 @@ void Position::clear() {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 16; j++)
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
board[sq] = NO_PIECE;
}
@@ -1463,7 +1367,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
value += piece_count(c, pt) * PieceValue[Mg][pt];
value += piece_count(c, pt) * PieceValue[MG][pt];
return value;
}
@@ -1472,7 +1376,6 @@ Value Position::compute_non_pawn_material(Color c) const {
/// Position::is_draw() tests whether the position is drawn by material,
/// repetition, or the 50 moves rule. It does not detect stalemates, this
/// must be done by the search.
template<bool SkipRepetition>
bool Position::is_draw() const {
// Draw by material?
@@ -1481,37 +1384,30 @@ bool Position::is_draw() const {
return true;
// Draw by the 50 moves rule?
if (st->rule50 > 99 && (!in_check() || MoveList<LEGAL>(*this).size()))
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
// Draw by repetition?
if (!SkipRepetition)
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (i <= e)
{
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
StateInfo* stp = st->previous->previous;
if (i <= e)
{
StateInfo* stp = st->previous->previous;
do {
stp = stp->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == st->key)
return true;
if (stp->key == st->key)
return true;
i += 2;
i +=2;
} while (i <= e);
}
} while (i <= e);
}
return false;
}
// Explicit template instantiations
template bool Position::is_draw<false>() const;
template bool Position::is_draw<true>() const;
/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs.
@@ -1526,7 +1422,7 @@ void Position::flip() {
thisThread = pos.this_thread();
nodes = pos.nodes_searched();
chess960 = pos.is_chess960();
startPosPly = pos.startpos_ply_counter();
gamePly = pos.game_ply();
for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.is_empty(s))
@@ -1593,7 +1489,7 @@ bool Position::pos_is_ok(int* failedStep) const {
if ((*step)++, debugKingCount)
{
int kingCount[2] = {};
int kingCount[COLOR_NB] = {};
for (Square s = SQ_A1; s <= SQ_H8; s++)
if (type_of(piece_on(s)) == KING)
+33 -41
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,7 +30,7 @@
/// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check.
class Position;
class Thread;
struct Thread;
struct CheckInfo {
@@ -37,19 +38,19 @@ 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 psqScore;
Square epSquare;
@@ -60,13 +61,10 @@ struct StateInfo {
StateInfo* previous;
};
struct ReducedStateInfo {
Key pawnKey, materialKey;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score psqScore;
Square epSquare;
};
/// 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:
@@ -95,13 +93,13 @@ class Position {
public:
Position() {}
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&);
// Text input/output
void from_fen(const std::string& fen, bool isChess960, Thread* th);
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;
// Position representation
Bitboard pieces() const;
@@ -124,7 +122,6 @@ public:
Square castle_rook_square(Color c, CastlingSide s) const;
// Checking
bool in_check() const;
Bitboard checkers() const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const;
@@ -139,8 +136,6 @@ public:
// Properties of moves
bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_attacks_square(Move m, Square s) const;
bool move_is_legal(const Move m) 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;
@@ -159,10 +154,11 @@ public:
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,12 +174,12 @@ public:
// Other properties of the position
Color side_to_move() const;
int startpos_ply_counter() const;
int game_ply() const;
bool is_chess960() const;
Thread* this_thread() const;
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
template<bool SkipRepetition> bool is_draw() const;
bool is_draw() const;
// Position consistency check, for debugging
bool pos_is_ok(int* failedStep = NULL) const;
@@ -195,8 +191,8 @@ private:
void put_piece(Piece p, Square s);
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)
@@ -209,20 +205,20 @@ private:
Value compute_non_pawn_material(Color c) const;
// Board and pieces
Piece board[64]; // [square]
Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color]
int pieceCount[2][8]; // [color][pieceType]
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
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[2][2]; // [color][side]
Bitboard castlePath[2][2]; // [color][side]
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;
Thread* thisThread;
StateInfo* st;
@@ -334,10 +330,6 @@ 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>();
}
@@ -384,8 +376,8 @@ inline bool Position::is_passed_pawn_push(Move m) const {
&& 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_bishops() const {
+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()); }
};
+664 -643
View File
File diff suppressed because it is too large Load Diff
+9 -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
@@ -38,15 +38,16 @@ 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 killers[2];
Depth reduction;
Value eval;
Value staticEval;
Value evalMargin;
int skipNullMove;
int futilityMoveCount;
};
@@ -56,12 +57,11 @@ struct Stack {
/// all non-pv moves.
struct RootMove {
RootMove(){} // Needed by sort()
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; }
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);
@@ -80,9 +80,9 @@ struct RootMove {
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(movetime | depth | nodes | infinite); }
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time[2], inc[2], movestogo, depth, nodes, movetime, infinite, ponder;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
};
@@ -98,7 +98,8 @@ typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<RootMove> RootMoves;
extern Position RootPosition;
extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
+140 -195
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,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::count
#include <cassert>
#include <iostream>
@@ -32,26 +33,24 @@ ThreadPool Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to member function pointed by start_fn.
// is launched. It is a wrapper to the virtual function idle_loop().
long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; }
long start_routine(Thread* th) { th->idle_loop(); return 0; }
} }
// Thread c'tor starts a newly-created thread of execution that will call
// the idle loop function pointed by start_fn going immediately to sleep.
// the the virtual function idle_loop(), going immediately to sleep.
Thread::Thread(Fn fn) {
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
is_searching = do_exit = false;
maxPly = splitPointsCnt = 0;
curSplitPoint = NULL;
start_fn = fn;
searching = exit = false;
maxPly = splitPointsSize = 0;
activeSplitPoint = NULL;
activePosition = NULL;
idx = Threads.size();
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
@@ -60,47 +59,49 @@ Thread::Thread(Fn fn) {
}
// Thread d'tor waits for thread termination before to return.
// Thread d'tor waits for thread termination before to return
Thread::~Thread() {
assert(do_sleep);
do_exit = true; // Search must be already finished
wake_up();
exit = true; // Search must be already finished
notify_one();
thread_join(handle); // Wait for thread termination
}
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls check_time(). If maxPly is 0 thread sleeps until is woken up.
// 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 Thread::timer_loop() {
void TimerThread::idle_loop() {
while (!do_exit)
while (!exit)
{
mutex.lock();
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
if (!exit)
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
mutex.unlock();
check_time();
if (msec)
check_time();
}
}
// Thread::main_loop() is where the main thread is parked waiting to be started
// 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 Thread::main_loop() {
void MainThread::idle_loop() {
while (true)
{
mutex.lock();
do_sleep = true; // Always return to sleep after a search
is_searching = false;
thinking = false;
while (do_sleep && !do_exit)
while (!thinking && !exit)
{
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
sleepCondition.wait(mutex);
@@ -108,22 +109,23 @@ void Thread::main_loop() {
mutex.unlock();
if (do_exit)
if (exit)
return;
is_searching = true;
searching = true;
Search::think();
assert(is_searching);
assert(searching);
searching = false;
}
}
// Thread::wake_up() wakes up the thread, normally at the beginning of the search
// or, if "sleeping threads" is used at split time.
// Thread::notify_one() wakes up the thread when there is some search to do
void Thread::wake_up() {
void Thread::notify_one() {
mutex.lock();
sleepCondition.notify_one();
@@ -131,19 +133,12 @@ void Thread::wake_up() {
}
// Thread::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.
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
void Thread::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
void Thread::wait_for(volatile const bool& b) {
mutex.lock();
while (!Signals.stop) sleepCondition.wait(mutex);;
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
@@ -153,7 +148,7 @@ void Thread::wait_for_stop_or_ponderhit() {
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = curSplitPoint; sp; sp = sp->parent)
for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint)
if (sp->cutoff)
return true;
@@ -164,46 +159,47 @@ bool Thread::cutoff_occurred() const {
// 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 active 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).
// 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(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 spCnt = splitPointsCnt;
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.
return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx));
return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx));
}
// init() is called at startup. Initializes lock and condition variable and
// launches requested threads sending them immediately to sleep. We cannot use
// 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.
// engine at this point due to allocation of Endgames in Thread c'tor.
void ThreadPool::init() {
timer = new Thread(&Thread::timer_loop);
threads.push_back(new Thread(&Thread::main_loop));
sleepWhileIdle = true;
timer = new TimerThread();
push_back(new MainThread());
read_uci_options();
}
// exit() cleanly terminates the threads before the program exits.
// exit() cleanly terminates the threads before the program exits
void ThreadPool::exit() {
for (size_t i = 0; i < threads.size(); i++)
delete threads[i];
delete timer; // As first because check_time() accesses threads data
delete timer;
for (iterator it = begin(); it != end(); ++it)
delete *it;
}
@@ -216,221 +212,170 @@ 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"];
assert(requested > 0);
while (threads.size() < requested)
threads.push_back(new Thread(&Thread::idle_loop));
while (size() < requested)
push_back(new Thread());
while (threads.size() > requested)
while (size() > requested)
{
delete threads.back();
threads.pop_back();
delete back();
pop_back();
}
}
// wake_up() is called before a new search to start the threads that are waiting
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
// threads will be woken up at split time.
// slave_available() tries to find an idle thread which is available as a slave
// for the thread 'master'.
void ThreadPool::wake_up() const {
Thread* ThreadPool::available_slave(Thread* master) const {
for (size_t i = 0; i < threads.size(); i++)
{
threads[i]->maxPly = 0;
threads[i]->do_sleep = false;
for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master))
return *it;
if (!useSleepingThreads)
threads[i]->wake_up();
}
}
// sleep() is called after the search finishes to ask all the threads but the
// main one to go waiting on a sleep condition.
void ThreadPool::sleep() const {
// Main thread will go to sleep by itself to avoid a race with start_searching()
for (size_t i = 1; i < threads.size(); i++)
threads[i]->do_sleep = true;
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread 'master'.
bool ThreadPool::available_slave_exists(Thread* master) const {
for (size_t i = 0; i < threads.size(); i++)
if (threads[i]->is_available_to(master))
return true;
return false;
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 ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Move* bestMove, 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);
Thread* master = pos.this_thread();
if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
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 = master->splitPoints[master->splitPointsCnt];
SplitPoint& sp = splitPoints[splitPointsSize];
sp.parent = master->curSplitPoint;
sp.master = master;
sp.cutoff = false;
sp.slavesMask = 1ULL << master->idx;
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.bestValue = bestValue;
sp.mp = mp;
sp.movePicker = movePicker;
sp.moveCount = moveCount;
sp.pos = &pos;
sp.nodes = 0;
sp.cutoff = false;
sp.ss = ss;
assert(master->is_searching);
master->curSplitPoint = &sp;
int slavesCnt = 0;
// 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.
Threads.mutex.lock();
sp.mutex.lock();
mutex.lock();
for (size_t i = 0; i < threads.size() && !Fake; ++i)
if (threads[i]->is_available_to(master))
{
sp.slavesMask |= 1ULL << i;
threads[i]->curSplitPoint = &sp;
threads[i]->is_searching = true; // Slave leaves idle_loop()
splitPointsSize++;
activeSplitPoint = &sp;
activePosition = NULL;
if (useSleepingThreads)
threads[i]->wake_up();
size_t slavesCnt = 1; // This thread is always included
Thread* slave;
if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
break;
}
master->splitPointsCnt++;
mutex.unlock();
sp.mutex.unlock();
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.
// 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.
if (slavesCnt || Fake)
if (slavesCnt > 1 || Fake)
{
master->idle_loop();
sp.mutex.unlock();
Threads.mutex.unlock();
Thread::idle_loop(); // Force a call to base class idle_loop()
// 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(!master->is_searching);
assert(!searching);
assert(!activePosition);
// 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();
}
// We have returned from the idle loop, which means that all threads are
// finished. Note that setting is_searching and decreasing splitPointsCnt is
// done under lock protection to avoid a race with Thread::is_available_to().
sp.mutex.lock(); // To protect sp.nodes
mutex.lock();
master->is_searching = true;
master->splitPointsCnt--;
master->curSplitPoint = sp.parent;
searching = true;
splitPointsSize--;
activeSplitPoint = sp.parentSplitPoint;
activePosition = &pos;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
*bestMove = sp.bestMove;
*bestValue = sp.bestValue;
mutex.unlock();
sp.mutex.unlock();
return sp.bestValue;
Threads.mutex.unlock();
}
// Explicit template instantiations
template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, 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);
// set_timer() is used to set the timer to trigger after msec milliseconds.
// If msec is 0 then timer is stopped.
// wait_for_think_finished() waits for main thread to go to sleep then returns
void ThreadPool::set_timer(int msec) {
void ThreadPool::wait_for_think_finished() {
timer->mutex.lock();
timer->maxPly = msec;
timer->sleepCondition.notify_one(); // Wake up and restart the timer
timer->mutex.unlock();
}
// wait_for_search_finished() waits for main thread to go to sleep, this means
// search is finished. Then returns.
void ThreadPool::wait_for_search_finished() {
Thread* t = main_thread();
MainThread* t = main_thread();
t->mutex.lock();
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
while (!t->do_sleep) sleepCondition.wait(t->mutex);
while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock();
}
// start_searching() wakes up the main thread sleeping in main_loop() so to start
// a new search, then returns immediately.
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
// so to start a new search, then returns immediately.
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_search_finished();
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_think_finished();
SearchTime = Time::now(); // As early as possible
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
RootPosition = pos;
RootPos = pos;
Limits = limits;
SetupStates = states; // Ownership transfer here
RootMoves.clear();
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (searchMoves.empty() || count(searchMoves.begin(), searchMoves.end(), ml.move()))
if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
main_thread()->do_sleep = false;
main_thread()->wake_up();
main_thread()->thinking = true;
main_thread()->notify_one(); // Starts main thread
}
+51 -52
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
@@ -28,7 +28,7 @@
#include "position.h"
#include "search.h"
const int MAX_THREADS = 32;
const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t
const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex {
@@ -56,22 +56,22 @@ private:
WaitCondition c;
};
class Thread;
struct Thread;
struct SplitPoint {
// Const data after split point has been setup
const Position* pos;
const Search::Stack* ss;
Thread* masterThread;
Depth depth;
Value beta;
int nodeType;
Thread* master;
Move threatMove;
// Const pointers to shared data
MovePicker* mp;
SplitPoint* parent;
MovePicker* movePicker;
SplitPoint* parentSplitPoint;
// Shared data
Mutex mutex;
@@ -90,77 +90,76 @@ struct SplitPoint {
/// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet.
class Thread {
struct Thread {
typedef void (Thread::* Fn) (); // Pointer to member function
Thread();
virtual ~Thread();
public:
Thread(Fn fn);
~Thread();
void wake_up();
virtual void idle_loop();
void notify_one();
bool cutoff_occurred() const;
bool is_available_to(Thread* master) const;
void idle_loop();
void main_loop();
void timer_loop();
void wait_for_stop_or_ponderhit();
void wait_for(volatile const bool& b);
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];
MaterialTable materialTable;
PawnTable pawnTable;
Material::Table materialTable;
Endgames endgames;
Pawns::Table pawnsTable;
Position* activePosition;
size_t idx;
int maxPly;
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
Fn start_fn;
SplitPoint* volatile curSplitPoint;
volatile int splitPointsCnt;
volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_exit;
SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize;
volatile bool searching;
volatile bool exit;
};
/// ThreadPool 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 ThreadPool {
struct ThreadPool : public std::vector<Thread*> {
public:
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.
Thread& operator[](size_t id) { return *threads[id]; }
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
size_t size() const { return threads.size(); }
Thread* main_thread() { return threads[0]; }
void wake_up() const;
void sleep() const;
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options();
bool available_slave_exists(Thread* master) const;
void set_timer(int msec);
void wait_for_search_finished();
void start_searching(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&);
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, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
friend class Thread;
std::vector<Thread*> threads;
Thread* timer;
bool sleepWhileIdle;
Depth minimumSplitDepth;
size_t maxThreadsPerSplitPoint;
Mutex mutex;
ConditionVariable sleepCondition;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
TimerThread* timer;
};
extern ThreadPool Threads;
+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 @@ 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
+1 -1
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 -44
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,35 +25,25 @@
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. Transposition table consists of a power of 2 number of
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each
/// non-empty entry contains information of exactly one position.
/// 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 = 1ULL << msb((mbSize << 20) / sizeof(TTCluster));
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
if (newSize == size)
uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));
if (hashMask == size - ClusterSize)
return;
size = newSize;
delete [] entries;
entries = new (std::nothrow) TTCluster[size];
hashMask = size - ClusterSize;
delete [] table;
table = new (std::nothrow) TTEntry[size];
if (!entries)
if (!table)
{
std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl;
@@ -70,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));
}
@@ -82,23 +72,23 @@ 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, Bound 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;
}
@@ -110,7 +100,7 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
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);
}
@@ -118,24 +108,14 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move
/// 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++;
}
+30 -41
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
@@ -44,7 +44,7 @@
class TTEntry {
public:
void save(uint32_t k, Value v, Bound b, 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;
@@ -52,63 +52,52 @@ public:
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; }
Bound type() const { return (Bound)bound; }
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 bound, generation8;
int16_t value16, depth16, staticValue, staticMargin;
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, Bound 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
};
@@ -119,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);
}
+38 -39
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
@@ -35,13 +35,14 @@
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
#include <cassert>
#include <cctype>
#include <climits>
#include <cstdlib>
#include "platform.h"
#if defined(_WIN64)
#if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
@@ -51,6 +52,10 @@
# 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
@@ -132,12 +137,20 @@ enum CastleRight { // Defined as in PolyGlot book hash key
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
QUEEN_SIDE,
CASTLING_SIDE_NB = 2
};
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
};
enum ScaleFactor {
@@ -168,8 +181,6 @@ enum Value {
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
Mg = 0, Eg = 1,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
@@ -179,17 +190,19 @@ enum Value {
enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6,
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 {
@@ -215,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,
@@ -229,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
};
@@ -320,9 +335,9 @@ inline Score apply_weight(Score v, Score w) {
namespace Zobrist {
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
extern Key enpassant[8]; // [file]
extern Key castle[16]; // [castleRight]
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;
@@ -331,9 +346,9 @@ namespace Zobrist {
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[16][64]; // [piece][square]
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType]
extern int SquareDistance[64][64]; // [square][square]
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;
@@ -377,6 +392,7 @@ inline PieceType type_of(Piece p) {
}
inline Color color_of(Piece p) {
assert(p != NO_PIECE);
return Color(p >> 3);
}
@@ -425,12 +441,12 @@ inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
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) {
@@ -459,7 +475,7 @@ inline Move make_move(Square from, Square to) {
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ;
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12));
}
inline bool is_ok(Move m) {
@@ -473,21 +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 first, K last)
{
T tmp;
K p, q;
for (p = first + 1; p < last; p++)
{
tmp = *p;
for (q = p; q != first && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
#endif // !defined(TYPES_H_INCLUDED)
+57 -94
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,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
@@ -26,7 +27,6 @@
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
using namespace std;
@@ -44,7 +44,7 @@ namespace {
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
void go(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up);
}
@@ -56,78 +56,32 @@ namespace {
void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main_thread()); // The root position
string cmd, token;
string token, cmd = args;
while (token != "quit")
{
if (!args.empty())
cmd = args;
else 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")
if (token == "quit" || token == "stop" || token == "ponderhit")
{
Search::Signals.stop = true;
Threads.wait_for_search_finished(); // Cannot quit while threads are running
}
else if (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)
// 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()->wake_up(); // Could be sleeping
Threads.main_thread()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
TT.clear();
else if (token == "isready")
sync_cout << "readyok" << sync_endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "bench")
benchmark(pos, is);
else if (token == "key")
sync_cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "perft" && (is >> token)) // Read depth
else if (token == "perft" && (is >> token)) // Read perft depth
{
stringstream ss;
@@ -136,16 +90,33 @@ void UCI::loop(const string& args) {
benchmark(pos, ss);
}
else if (token == "key")
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")
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
sync_cout << "Unknown command: " << cmd << sync_endl;
if (!args.empty()) // Command line arguments have one-shot behaviour
{
Threads.wait_for_search_finished();
break;
}
}
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
Threads.wait_for_think_finished(); // Cannot quit while search is running
}
@@ -174,7 +145,7 @@ namespace {
else
return;
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any)
@@ -211,10 +182,10 @@ namespace {
// 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 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) {
Search::LimitsType limits;
vector<Move> searchMoves;
@@ -222,31 +193,23 @@ namespace {
while (is >> token)
{
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 == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
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;
}
Threads.start_searching(pos, limits, searchMoves, SetupStates);
Threads.start_thinking(pos, limits, searchMoves, SetupStates);
}
}
+5 -4
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
@@ -64,6 +64,7 @@ void init(OptionsMap& o) {
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);
@@ -71,11 +72,11 @@ void init(OptionsMap& o) {
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, 7, on_threads);
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(true, on_threads);
o["Hash"] = Option(32, 4, 8192, on_hash_size);
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);
+1 -1
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