Compare commits

..

124 Commits

Author SHA1 Message Date
Marco Costalba c5bb9b9da9 Stockfish DD
Stockfish bench signature is: 8596156
2013-11-29 10:23:14 +01:00
Kelly Wilson 67e5581e37 Add support for PPC 64bit on Linux
In particular Debian Linux-3.9.8-1- PPC64

No functional change.
2013-11-29 10:06:54 +01:00
Joona Kiiski 3ede7daab3 Generate Qsearch checks only at depth 0
An old idea retested at SPRT(0, 3) with 60+0.05 TC:
LLR: 2.95 (-2.94,2.94) [0.00,3.00]
Total: 98872 W: 15549 L: 15123 D: 68200

This is a very small elo increase patch so it really
stresses the limits of fishtest.

bench: 8596156
2013-11-24 10:26:49 +01:00
Marco Costalba dbd6156fce Revert previous fix
It seems to intorduce a regression when tested
with 3 threads at 15+0.05:

ELO: -2.26 +-2.2 (95%) LOS: 2.4%
Total: 30000 W: 4813 L: 5008 D: 20179

bench: 8331357
2013-11-19 07:20:50 +01:00
Hongzhi Cheng 691ed425ba Get correct excluded moves for split nodes
Tested setting FakeSplit to true and running

./stockfish bench 128 2

There is a different signature with and without
the patch so it affects functionality but
only in SMP case.

bench: 8331357
2013-11-18 16:41:49 +01:00
Marco Costalba c376ffce0f Revert previous patch
It seems a regression at 15+0.05:
ELO: -4.82 +-2.1 (95%) LOS: 0.0%
Total: 40000 W: 7181 L: 7736 D: 25083

bench: 8331357
2013-11-17 23:47:18 +01:00
Marco Costalba 917944e9c5 Fix an assert in SMP case
SMP case is very tricky and raises an assert in stage_moves():

assert(stage == KILLERS_S1 || stage == QUIETS_1_S1 || stage == QUIETS_2_S1)

So rewrite the code to just return moves[] when we are sure
we are in quiet moves stages.

Also rename stage_moves to quiet_moves to reflect that.

No functional change (but needs testing in SMP case)
2013-11-17 10:24:25 +01:00
Marco Costalba d9c7cad630 Retire quietsSearched[]
Use MovePicker moves[] to access already tried
quiet moves. A bit of care shall be taken
to avoid calling stage_moves() when we are still
at ttMove stage, because moves are yet to be
generated. Actually our staging move generation
makes this code a bit more tricky than what I'd
like, but removing an ausiliary redundant
array like quietsSearched[] is a good thing.

Idea by DiscoCheck

bench: 9355734
2013-11-17 09:51:04 +01:00
Marco Costalba 9763c69fa5 Simplify generate<EVASIONS>
Use the newly introduced LineBB[] to simplify this
super hot-path function.

Verified with perft we don't have any speed regression, although
the number of squares removed is less than before in case of
contact check.

Insipred by DiscoCheck implementation.

Perft numbers are the same, but we have an harmless functional
change due to reorder of moves, because now some illegal moves
are no more detected at generation time, but in the search.

bench: 8331357
2013-11-11 19:53:19 +01:00
Gregor Cramer 555d9a8711 Faster castling in Chess960 case
Only rook attackers has to be considered, all other attackers are
already handled in the lines above.

No functional change.
2013-11-11 15:55:08 +01:00
Joona Kiiski b9768b8bc5 Reintroduce gains
This seems a die hard idea :-)

Passed both short TC
LLR: 2.97 (-2.94,2.94) [-1.50,4.50]
Total: 17485 W: 3307 L: 3156 D: 11022

And long TC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 38181 W: 6002 L: 5729 D: 26450

bench: 8659830
2013-11-11 08:46:11 +01:00
Jörg Oster 4ef6b2c32a Remove opposed flag for doubled pawns
Actually, it is not used, as both arrays have the
same values. Some local tests in either direction
showed no improvement.

Also some minor corrections in the comments.

No functional change.
2013-11-10 17:21:59 +01:00
Marco Costalba 1d18647e73 Rename squares_aligned()
Rename to the shorter but still
clear aligned()

No functional change.
2013-11-10 17:14:46 +01:00
Marco Costalba a518d5d3ad Simplify squares_aligned()
Use newly introduced LineBB[]

No functional change.
2013-11-10 12:05:19 +01:00
Chris Caino 091aff0445 Evaluate mobility of pinned pieces exactly
Previously some squares could be "incorrectly" awarded
to a pinned piece.

e.g. in 3k4/1q6/3b4/3Q4/8/5K2/B7/8 b - - 0 1 the black
bishop get 4 squares too many and the white queen gets 6.

Passed both short TC.
LLR: 2.97 (-2.94,2.94) [-1.50,4.50]
Total: 4871 W: 934 L: 817 D: 3120

And long TC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 38968 W: 6113 L: 5837 D: 27018

bench: 9282549
2013-11-10 11:52:38 +01:00
Chris Caino 3ed86ed3f9 Remove RedundantMajor
But compensate by reducing rook and queen
value by 53 = (160 / 3)

Material imbalances are affected as follows:

       Red. Major   Rook  Queen  Total
QRR      +160      -2*53    -53     +1
QR       +160        -53    -53    +54
RR       +160      -2*53      0    +54
R           0        -53      0    -53
Q           0          0    -53    -53

so that the imbalance changes by at most 54 + 53 = 107 units.
This corresponds to appromximately 3.5cp in the final evaluation.

Verified with fixed number 40000 games at both short
and long TC it does not regress.

Short TC 15+0.05
ELO: 1.93 +-2.1 (95%) LOS: 96.6%
Total: 40000 W: 7520 L: 7298 D: 25182

Long TC 60+0.05
ELO: -0.33 +-1.9 (95%) LOS: 36.5%
Total: 39663 W: 6067 L: 6105 D: 27491

bench: 6703846
2013-11-10 09:48:49 +01:00
Marco Costalba e4d34e1815 Fix printing of incorrect PV in some cases
As, Gary (that analyzed the bug) says:

SF does not print a PV when the original best move fails low,
we hit our time allowance, and stop the search.  The output from
the SF search is below.  It was failing low on Ne1 at depth 34.
Then, we get bestmove Qd3, but no PV change.

info depth 34 seldepth 45 score cp 38 upperbound nodes 483484489 nps 15464575 time 31264 multipv 1 pv f3e1 h5h4 e1d3 h4g3 f2g3 a6f6 f1f6 e7f6 d1a4 f6e7 a1f1 d8f8 a4b3 b7b6 b3c2 f7f6 c2a4 h3g5 b2b3 g5f7 a4c6 f7d6 h1g2 f6f5 e4f5 d6f5
info depth 34 seldepth 45 score cp 38 upperbound nodes 483484489 nps 15464575 time 31264 multipv 1 pv f3e1 h5h4 e1d3 h4g3 f2g3 a6f6 f1f6 e7f6 d1a4 f6e7 a1f1 d8f8 a4b3 b7b6 b3c2 f7f6 c2a4 h3g5 b2b3 g5f7 a4c6 f7d6 h1g2 f6f5 e4f5 d6f5
info depth 34 seldepth 47 score cp 30 upperbound nodes 2112334132 nps 17255517 time 122415 multipv 1 pv f3e1 h5h4 d1a4 a6f6 e1d3 d8f8 a4c2 h4g3 f2g3 f6f1 a1f1 h7g8 b2b3 f7f6 a3a4 b7b6
info depth 34 seldepth 47 score cp 30 upperbound nodes 2112334132 nps 17255517 time 122415 multipv 1 pv f3e1 h5h4 d1a4 a6f6 e1d3 d8f8 a4c2 h4g3 f2g3 f6f1 a1f1 h7g8 b2b3 f7f6 a3a4 b7b6
info nodes 18235667001 time 969824
bestmove e2d3 ponder c8d7

Looking at the code, if we hit Signals.stop, we return from id_loop
before printing any PV.  It is possible for us to have resorted the
RootMove list though, which will change the move that is actually
played.

No functional change.
2013-11-09 19:05:43 +01:00
Marco Costalba 42caebfaa5 Fix compile in debug mode
No functional change.
2013-11-09 18:41:51 +01:00
Lucas Braesch eed508b444 Futility pruning simplification
1/ eval margin and gains removed:
16bit are now free on TT entries, due to the removal of eval margin. may be useful
in the future :) gains removed: use instead by Value(128). search() and qsearch()
are now consistent in this regard.

2/ futility_margin()
linear formula instead of complex (log(depth), movecount) formula.

3/ unify pre & post futility pruning
pre futility pruning used depth < 7 plies, while post futility pruning used
depth < 4 plies. Now it's always depth < 7.

Tested with fixed number of games both at short TC:
ELO: 0.82 +-2.1 (95%) LOS: 77.3%
Total: 40000 W: 7939 L: 7845 D: 24216

And long TC
ELO: 0.59 +-2.0 (95%) LOS: 71.9%
Total: 40000 W: 6876 L: 6808 D: 26316

bench 7243575
2013-11-09 10:17:27 +01:00
Marco Costalba 343544f3f7 Revert "Retire eval margin and gains"
This reverts commit ecd07e51d0.

Patch was incorrect and partial. It will be reapplied in
the correct form.

bench: 9189063
2013-11-07 22:32:13 +01:00
Gary Linscott 13d1f0ae43 Restrict mobility of pinned pieces
Passed both short TC:
LLR: 3.00 (-2.94,2.94) [-1.50,4.50]
Total: 54342 W: 10950 L: 10692 D: 32700

And long TC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 61976 W: 10654 L: 10251 D: 41071

This patch introduces a slowdown of 3.5 % !!!!!

bench: 7911558
2013-11-07 22:26:03 +01:00
Lucas Braesch ecd07e51d0 Retire eval margin and gains
1/ eval margin and gains removed:
 - gains removed by Value(128): search() and qsearch() now behave consistently!

2/ futility_margin()
 - testing showed that there is no added value in this weird (log(depth), movecount)
   formula, and a much simpler linear formula is just as good. In fact, it is most
   likely better, as it is not yet optimally tuned.
 - the new simplified formula also means we get rid of FutilityMargins[], its
   initialization code, and more importantly ss->futilityMoveCount, and the hacky
   code that updates it throughout the search().
 - the current formula gives negative futility margins, and there is a hidden interaction
   between the move coutn pruning formula and the futility margin one: what happens is
   that MCP is supposed to be triggered before we use the non-sensical negative futility
   margins.

3/ unify pre & post futility pruning
 - pre futility pruning (what SF calls value based pruning) used depth < 7 plies,
   while post futility pruning (what SF calls static null move pruning) used depth < 4 plies.
 - also the condition depth < 7 in pre futility pruning was not obvious, and it seemd
   to be depth < 16 (futility_margin() returns an infinite value when depth >= 7).

Tested with fixed number of games both at short TC:
ELO: 0.82 +-2.1 (95%) LOS: 77.3%
Total: 40000 W: 7939 L: 7845 D: 24216

And long TC
ELO: 0.59 +-2.0 (95%) LOS: 71.9%
Total: 40000 W: 6876 L: 6808 D: 26316

bench: 10206576
2013-11-07 19:46:51 +01:00
Chris Caino 52ae0efccf Two more parameters eliminated
RedundantRook and RedundantQueen replaced by simple
variable RedundantMajor. Also the SameColor coefficient
for Queen<->Queen has been set by definition to 0.

The remaining 5 parameters:

LinearCoefficients[ROOK]
LinearCoefficients[QUEEN]
QuadraticCoefficientsSameColor[ROOK][ROOK]
QuadraticCoefficientsSameColor[QUEEN][ROOK]
RedundantMajor

are sufficient to equate the material imbalances for the
5 common material configurations of R, RR, Q, QR and QRR
to any desired values simultaneously.

With the chosen parameters there should be no functional
change unless one side has more than 2 rooks or more
than 1 queen. For example bench from the start position
using the commands:

./stockfish
go depth 16

produces identical output except for one extra node
in the last iteration.

bench: 8198094
2013-11-07 19:20:24 +01:00
Chris Caino 53c04c0429 Zero more redundant coefficients
Coefficients for Bishop<->BishopPair and Bishop<->Bishop
are also pretty much redundant. By altering the values
in LinearCoefficients[] these coefficients can be zeroed
without changing the imbalance calculations in any position
with less than 3 bishops for one side.

bench: 7995098
2013-11-05 20:08:39 +01:00
Chris Caino 1064288b38 Zero redundant material imbalance terms
First coefficient in the SameColor array does an
equivalent job when folded into the LinearCoefficients
array.

All of the diagonal terms in the OppositeColor array
are redundant due to cancellation.

No functional change.
2013-11-05 20:08:39 +01:00
Joona Kiiski d34bb889b1 Test Easy Move if no BestMoveChanges
In case we find a very good move after a
troubled start, we don't return immediately
anymore.

Tested directly at long TC where it passed:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 13910 W: 2397 L: 2228 D: 9285

bench: 7995098
2013-11-02 11:34:42 +01:00
Marco Costalba a3a0df92a3 Set timer to a fixed interval
And remove a complex (and broken) formula.

Indeed previous code was broken in case of TC with big
time increments where available_time() was too similar
to total time yielding to many time losses, so for instance:

go wtime 2600 winc 2600
info nodes 4432770 time 2601 <-- time forfeit!

maximum search time = 2530 ms
available_time = 2300 ms

For a reference and further details see:

https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/dCPAvQDcm2E

Speed tested with bench disabling timer alltogheter vs timer set at
max resolution, showed we have no speed regressions both in single
core and when using all physical cores.

No functional change.
2013-11-01 08:56:15 +01:00
Ralph Ster e8f9447b11 Use a formula for chain membership bonus
Passed both short TC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 5087 W: 1072 L: 951 D: 3064

And long TC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 28620 W: 5042 L: 4798 D: 18780

bench: 7995098
2013-10-31 06:13:30 +01:00
Marco Costalba 3cc47edf62 Tweak bishop pair and knight weight
A combo of two patches that failed SPRT with score
higher than 50% but togheter they succeed:

SPRT at 60+0.05
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 7312 W: 1276 L: 1139 D: 4897

bench: 8029334
2013-10-28 19:31:25 +01:00
Matthew Sullivan d454cd4216 Fix divide by zero bug in late game
If the game got late enough that move_importance(currentPly) * slowMover / 100
rounds to 0, then we ended up dividing 0 by 0 when only looking 1 move ahead.

This apparently caused the search to almost immediately abort and Stockfish
would blunder in long games. So convert thisMoveImportance to a double.

No functional change.
2013-10-27 08:03:58 +01:00
Marco Costalba 48f38f3092 Retire mirror()
Inline the only caller site.

No functional change.
2013-10-24 20:40:26 +02:00
Marco Costalba 281472e50e Prefer file_bb() to FileBB[]
No functional change.
2013-10-24 20:34:23 +02:00
Jörg Oster f011a5af11 Penalty for Knight when enemy pawns are few
This seems more a material imbalance topic,
anyhow test is good and so patch is applied
as is.

Passed both short TC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 17391 W: 3548 L: 3393 D: 10450

And long TC:
LLR: 3.00 (-2.94,2.94) [0.00,6.00]
Total: 34660 W: 5972 L: 5700 D: 22988

bench: 8291883
2013-10-24 20:11:33 +02:00
Marco Costalba 67b0da83da Further smplify pawn endgames
Dumb down a bit the code and trade some possible
speed (but this is far from hot path anyhow) for
some added readability for the layman.

No functional change.
2013-10-23 19:56:35 +02:00
Chris Caino 3674f18b97 Use flip_sq idea in endgame.cpp
The normalising transformation is computed all at
once by the helper function get_flip_sq and then
applied immediately to the relevant squares as soon
as they are loaded from the position class.

bench: 8350690
2013-10-23 16:00:49 +02:00
Chris Caino 72f7282ad4 Simplify futility move count formula
Simpler formula but introduces some slight changes if d >= 10

Original code grows like  0.225 * d^1.8
New code grows like       0.222 * d^1.8

Full list of values:

d old new diff
--------------
0 2 2 0
1 2 2 0
2 3 3 0
3 4 4 0
4 5 5 0
5 6 6 0
6 7 7 0
7 9 9 0
8 11 11 0
9 13 13 0
10 15 16 1
11 18 19 1
12 21 21 0
13 24 24 0
14 27 28 1
15 31 31 0
16 35 35 0
17 39 38 -1
18 42 42 0
19 47 46 -1
20 51 51 0
21 55 55 0
22 60 60 0
23 65 65 0
24 70 70 0
25 75 75 0
26 81 80 -1
27 87 86 -1
28 92 91 -1
29 98 97 -1
30 104 103 -1
31 111 109 -2

Test code:

int main() {

  for(int d=0; d<32; d++)
  {
     int a = int(3 + 0.3 * pow(double(d), 1.8)) * 3/4 + (2 < d && d < 5);
     int b = int(2.4 + 0.222 * pow(d + 0.0, 1.8));

     std::cout << d << " " << a << " " << b << " " << b-a << std::endl;
  }

  return 0;
}

bench: 8350690
2013-10-22 23:09:40 +02:00
Chris Caino fbfce2132a Simplify futility margins formula
New formula mathces the old formula until d = 45

Test code:

int main() {

  for(int d=1; d<=45; d++)
  {
     int a = int(log(double(d * d) / 2) / log(2.0) + 1.001);
     int b = int(2.9 * log(double(d)));

     if (a != b) std::cout << d << std::endl;
  }

  return 0;
}

bench: 8455956
2013-10-22 23:06:06 +02:00
Marco Costalba 2c825294ec Tweak again chain pawn bonus
This is the first chain bonus version
from Ralph that also passed both

Short TC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 23460 W: 4727 L: 4556 D: 14177

And long TC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 31858 W: 5497 L: 5240 D: 21121

And performed better against current
committed version, always at 60secs:

LLR: -2.94 (-2.94,2.94) [-3.00,3.00]
Total: 26301 W: 4477 L: 4580 D: 17244

This test was done by Leonid.

bench: 8455956
2013-10-22 17:47:16 +02:00
Marco Costalba f86d2aee29 Re-add "Further increase safe checks bonus"
After 40K games at 60 secs, result is still
not clear, but not a regression against SF 4

After
ELO: 50.11 +-2.1 (95%) LOS: 100.0%
Total: 40000 W: 10547 L: 4817 D: 24636

Before
ELO: 49.51 +-2.1 (95%) LOS: 100.0%
Total: 40000 W: 10483 L: 4821 D: 24696

So re-apply the patch to avoid to
special-case this one.

bench: 7403882
2013-10-22 17:33:11 +02:00
Marco Costalba 35ea39bed2 Restore behaviour after count<ALL_PIECES> fix
Because pos.count<ALL_PIECES>(Us) was always zero,
rewrite the formula as if this would still be
the case.

bench: 8510004
2013-10-22 17:27:58 +02:00
Ralph Stößer 97015afce8 Further improve chain pawn evaluation
Passed both short TC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 28299 W: 5854 L: 5667 D: 16778

And long TC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 9738 W: 1797 L: 1644 D: 6297

bench: 9294116
2013-10-20 23:41:40 +02:00
Marco Costalba f22a63ce67 Fix pos.count<ALL_PIECES>()
It was never updated !

Currently it only affects evaluate_passed_pawns()
and in particularly the rule to increase the bonus
if we have more non-pawn pieces. We could simply use
popcount() instead and avoid the little slowdown
in put_piece() and remove_piece(), but this would
leave a very subtle and tricky hole where people
are forced to remember that pos.count<ALL_PIECES>()
does not work. This is not obvious and so dangerous.

Thanks to Ronald de Man for spotting this.

bench: 7931424
2013-10-20 23:36:46 +02:00
Marco Costalba c08e7419a0 Fix build on Intel compiler
Due to a strange issue (bug?) the ternary
operator does not return a BitCountType for
icc, so revert to the expression.

The same patch was already applied in
9749f1f14c

Thanks to NssY Wanyonyi for pointing out
this.

No functional change.
2013-10-20 23:19:08 +02:00
Marco Costalba 67f91bc5ea Revert "Further increase safe checks bonus"
This reverts commit 4bc2374450 for
two reasons.

First regression testing shows almost equal
score:

Before the patch:
ELO: 49.75 +-2.5 (95%) LOS: 100.0%
Total: 27205 W: 7113 L: 3244 D: 16848

After the patch:
ELO: 48.87 +-2.9 (95%) LOS: 100.0%
Total: 20860 W: 5478 L: 2563 D: 12819

Second, and more sensible to me, this patch
increases safe check bonuses to 4 times their
original value (!) and considering:

- Values were already well tuned

- Values are highly critical

- King safety is highly critical, very TC
  dependent and very difficult to test

- Our testing coverage is partial (self-testing,
  blitz times)

I think is better to be safe than sorry and so
I revert the patch.

bench: 8440524
2013-10-20 10:04:43 +02:00
Ralph Stößer 4bc2374450 Further increase safe checks bonus
Passed both short TC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 10466 W: 2087 L: 1953 D: 6426

And long TC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 26334 W: 4540 L: 4310 D: 17484

And also proved stronger than a slightly
different patch, also succesful against master:

https://github.com/mcostalba/Stockfish/commit/dc6830a3b4ed12

But losing against current one in a match
at 60secs with SPRT [-3, 3]:

LLR: -2.96 (-2.94,2.94) [-3.00,3.00]
Total: 44484 W: 7360 L: 7463 D: 29661

bench: 9160831
2013-10-19 12:19:36 +02:00
Marco Costalba f5e872a0e3 Some evaluation code reshuffle
No functional change.
2013-10-18 09:49:38 -07:00
Jörg Oster 25cb851f8a Score chain pawn also by rank
Use the (rescaled) CandidatePassed[] table
that is already rank based.

Passed both short TC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 11048 W: 2272 L: 2135 D: 6641

And long TC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 4116 W: 769 L: 645 D: 2702

bench: 8440524
2013-10-18 10:27:55 +02:00
Chris Caino 3cddb0c076 Simplification of KPsK function
Also the drawing criteria has been slightly loosened.
It now detects a draw if the king is ahead of all the
pawns and on the same file or the adjacent file.

bench: 7700683
2013-10-15 07:36:01 +02:00
Chris Caino 2bf18bfc63 Bug fix for KQKRPs endgame
This lost position 8/8/3q4/8/5k2/2P1R3/2K2P2/8 w - - 0 1
was previously evaluated as a draw.

The king and rook need to be correctly placed with
respect to the _same_ pawn.

(Note also that the check for the pawn being on RANK_2
in the old version is redundant: it must be on RANK_2 if
it hopes to protect a rook on RANK_3)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-10-15 07:34:29 +02:00
Ralph Stößer 5aeb907fa1 Double king safety weights
Good both at short TC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 5448 W: 1133 L: 1012 D: 3303

And at long TC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 40509 W: 6836 L: 6541 D: 27132

bench: 7700683
2013-10-14 23:24:29 +02:00
Chris Caino 0c68971c13 Remove a drawing rule from KBPsK function
The rule can be incorrect if the attacking king is
well placed e.g. 8/6K1/8/8/7k/1B6/7P/8 w - - 0 1

bench: 8279065
2013-10-14 19:55:07 +02:00
Marco Costalba d9be00342c Massive stronger/weaker renming
No functional change.
2013-10-14 19:38:08 +02:00
Chris Caino 027d85e82a Add helper function verify_material
Allows to remove a lot of assert code in endgames.

No functional change.
2013-10-14 19:23:57 +02:00
ceebo 3bc3c069f1 Add some knowledge for KRPKB endgame
bench: 8279065
2013-10-14 07:44:02 +02:00
ceebo e9366fa155 Improve KBPsK endgame
Better endgame with bishop and blocked g-pawn

bench: 8279065
2013-10-14 07:44:01 +02:00
Marco Costalba 549b5c478f Remove unuseful optimization in RKISS
Don't need a struct here. Speed test shows
result is teh same. Moreover RKISS is used
mainly at startup to compute magics, so
prefer to keep it simple...RKISS ;-)

Also some assorted triviality while there.

No functional change.
2013-10-13 03:35:17 -07:00
Joona Kiiski b15e148b5e Smoother transition for LMR
Passed both short TC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 12376 W: 2596 L: 2454 D: 7326

And long TC:
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 14798 W: 2584 L: 2409 D: 9805

bench: 8279065
2013-10-09 19:13:41 +02:00
Uri Blass bb83a417cb Increase slowmover and reduce instability
These two changes go in opposite directions and it
seems that the combination is stronger than original.

Here are the positive tests at various TC:

15+0.05
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 24561 W: 4946 L: 4772 D: 14843

60+0.05
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 15259 W: 2598 L: 2423 D: 10238

40/30
LLR: 2.96 (-2.94,2.94) [-3.00,3.00]
Total: 2570 W: 527 L: 422 D: 1621

Unfortunately there is also a bad result
with one sec time increment that needs
to be further investigated:

12+1
LLR: -2.97 (-2.94,2.94) [-3.00,3.00]
Total: 2694 W: 438 L: 543 D: 1713

bench: 8340585
2013-10-08 21:24:21 +02:00
Lucas Braesch 984ee9d05b Use TT refined value to stand pat
Passed both short TC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 17811 W: 3520 L: 3366 D: 10925

And long TC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 30255 W: 5070 L: 4825 D: 20360

bench: 8340585
2013-10-08 19:51:08 +02:00
Lucas Braesch a0cc15ccbc Use double everywhere
Rationale:

- Speed of double and float is about the same (not on the hot path anyway)

- Double makes code prettier (no need to write 1.0f, just 1.0)

- Only practical advantage of float is to use less memory, but since we never
  store large arrays of double, we don't care.

No functional change.
2013-10-05 18:12:52 +02:00
Lucas Braesch 7f142d6817 Use prefix operators wherever possible
No functional change.
2013-10-05 18:10:43 +02:00
Marco Costalba bd1c3ed7e3 Add more depth/positions to bench
Increase bench default depth from 12 to 13 and
add 15 new endgame positions to have broader
coverage and also more reliable nps calulcation
used for fishtest framework.

Due to the new endgame positions, where nps is higher,
the total nps is increased of about 15%.

Thanks to Lucas and Jörg for the suggestions.

No functional change, but bench number is now:

bench: 8336338
2013-09-29 09:43:10 +02:00
Marco Costalba cca34e234c Drop 'is' prefix from query functions
Most but not all.

No functional change.
2013-09-28 06:47:59 -07:00
Marco Costalba ed95ad1c0e Fix build on Mac OS X
For some users -stack_size,0x4000 does not work,
so revert for now.

osX 10.6.8
gcc version 4.7.3 (MacPorts gcc47 4.7.3_2)

g++: error: unrecognized command line option '-stack_size,0x4000'
make[2]: *** [stockfish] Error 1
make[1]: *** [gcc-profile-make] Error 2
make: *** [profile-build] Error 2

No functional change.
2013-09-28 04:16:16 -07:00
Marco Costalba c65d67feb5 Revert "Use a per-thread array"
This reverts commit 800410eef1 and instead increases
stack size.

I went through the old emails with Daylen that reported the
crash issue on Mac OS X and was fixed by 0049d3f337.

It was reported default stack size for a thread in Mac OS X is 8
megabytes while the patch that we are reverting allows to reduce
stack size at max of about 217KB, so the reason for the crash was
only marginal in MAX_MOVES value. On those emails Daylen also
hinted how to increase stack size for Mac OS X to 16MB.

So prefer to increase stack size to 16MB instad of re-inventing
the wheel and do our home grown stack as we did with the patch
that we are now reverting (it will remain anyhow in git history
for documentation purposes).

No functional change.
2013-09-28 10:10:51 +02:00
Lucas Braesch bc6faf633e Simplify extensions
Unify extensions between PV and not PV nodes
and remove all but check extensions.

This is a simplification so tested at fixed number
of games where proved to not regress.

About 45k games at 15+0.05
ELO: 1.23 +-2.0 (95%) LOS: 88.5%
Total: 45643 W: 9107 L: 8946 D: 27590

About 45k games at 60+0.05
ELO: 1.07 +-1.8 (95%) LOS: 87.8%
Total: 46786 W: 7728 L: 7584 D: 31474

bench: 3172206
2013-09-28 09:54:22 +02:00
Reuven Peleg 8d1c1074d5 Simplify tte use condition
No functional change.
2013-09-27 09:40:48 +02:00
Raminder Singh e654209211 Fix best move lookup bug
If the uci option 'Best Book Move' is set to true the lookup still
returns a move at random instead of the move with the highest
weight.

No functional change.
2013-09-27 09:04:24 +02:00
Marco Costalba b742a3f29a Increase MAX_MOVES to 256
This should be enough for any legal position, even
the handcrafted ones, like the one presented by Reuven:

1Q5R/4Q1K1/B1Q5/B4Q2/N2Q4/pQ4Q1/pn2Q3/krQ4R w - -

Where currently we crash. This reverts the patch
0049d3f337 of 8/4/2012 where stack
was shrinked due to crashes while in deep analysys.

No functional change.
2013-09-27 08:59:03 +02:00
Marco Costalba 800410eef1 Use a per-thread array for generated moves
This greately reduces stack usage and is a
prerequisite for next patch.

Verified with 40K games both in single and SMP
case that there are no regressions.

No functional change.
2013-09-27 08:44:36 +02:00
Jean-Francois Romang 7b2cda95d9 Update disabled warnings for Intel compiler
No functional change.
2013-09-23 08:11:43 +02:00
Jean-Francois Romang c2cefa6de0 Replace -O3 with -fast for intel compiler
No functional change.
2013-09-23 08:11:14 +02:00
Ralph Stößer d7f5f15d69 Reduce negative quiets by ONE_PLY / 2
Passed both short TC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 3402 W: 708 L: 593 D: 2101

And long TC:
LLR: 2.93 (-2.94,2.94) [0.00,6.00]
Total: 23379 W: 3972 L: 3759 D: 15648

bench: 3529630
2013-09-23 08:08:52 +02:00
Marco Costalba 84906b83ec Final time management setup
This is an even safer setup proposed and tested
by Alexandre Meirelles.

Regression testing of 40K games at 10+0.05 show
result is stable both against current master:

ELO: -0.29 +-2.2 (95%) LOS: 39.7%
Total: 40000 W: 8010 L: 8043 D: 23947

and again original master (the one with smallest
time parameters):

ELO: 1.71 +-2.2 (95%) LOS: 93.8%
Total: 40000 W: 8325 L: 8128 D: 23547

Alexandre verified with LittleBlitzer time losses are
greately reduced with this setup:

Games Completed = 2100 of 3000 (Avg game length = 35.745 sec)

Settings = RR/128MB/15000ms+50ms/M 1000cp for 12 moves, D 150 moves/
Time = 39200 sec elapsed, 16800 sec remaining
 1.  Stockfish 190913             1091.5/2100    803-720-577      (L: m=313 t=1 i=0 a=406)    (D: r=278 i=91 f=136 s=8 a=64)    (tpm=212.5 d=14.75 nps=925427)
 2.  Houdini 2.0 w32              1008.5/2100    720-803-577      (L: m=250 t=299 i=0 a=254)    (D: r=278 i=91 f=136 s=8 a=64)    (tpm=204.1 d=12.04 nps=1326351)

No functional change.
2013-09-23 07:59:51 +02:00
Marco Costalba 274079990a Increase Emergency Move Time to 20
Goes in the direction of avoiding time losses and seems
equivalent after almost 40K games at super fast TC of 10+0.05

ELO: 2.61 +-2.2 (95%) LOS: 99.1%
Total: 39869 W: 8258 L: 7959 D: 23652

No functional change.
2013-09-19 07:26:36 +02:00
Marco Costalba 10cb19d534 Increase Emergency Move Time to 10
Goes in the direction of avoiding time losses and seems
equivalent after almost 40K games at super fast TC of 10+0.05

ELO: 2.41 +-2.3 (95%) LOS: 98.1%
Total: 37222 W: 7843 L: 7585 D: 21794

No functional change.
2013-09-17 16:32:39 +02:00
Marco Costalba d50b33cacd Fix a silly unstoppable eval bug
The logic is broken for black side because we get more bonus
for pawn in 7th rank than for pawn in 2nd rank!

Spotted by Reuven Peleg

bench:3884409
2013-09-16 23:59:37 +02:00
Joona Kiiski 77b5ee0117 Fix time parameters for blitz games
The ideal setting for super-blitz might be something like:

    "Emergency Base Time" = 50
    "Emergency Move Time" = 5

This would give a total emergency time buffer of:

    50 + 40 * 5 = 250 ms

This setup replaces the previous half cooked hack
"Don't blunder under extreme time pressure".

Test results are very good at super blitz, but keep good even
at 60 secs.

At 5+0.05
ELO: 24.30 +-2.4 (95%) LOS: 100.0%
Total: 37802 W: 10060 L: 7420 D: 20322

At 15+0.05
ELO: 13.41 +-2.9 (95%) LOS: 100.0%
Total: 22271 W: 4853 L: 3994 D: 13424

At 60+0.05
ELO: 5.30 +-3.2 (95%) LOS: 99.9%
Total: 16000 W: 2897 L: 2653 D: 10450

No functional change.
2013-09-16 09:07:47 +02:00
Marco Costalba af750bd2ef Rewrite unstoppable pawns evaluation
Instead of current code, give a bonus according to the frontmost
square among candidate + passed pawns.

This is a big simplification that removes a lot of accurate code
substituting it with a statistically based one using the common
'bonus' scheme, leaving to the search to sort out the details.

Results are equivalent but code is much less and, as an added bonus,
we now store candidates bitboard in pawns hash and allow this
info to be used in evaluation. This paves the way to possible
candidate pawns evaluations together with all the other pieces,
as we do for passed.

Patch passed short TC
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 16927 W: 3462 L: 3308 D: 10157

Then failed (quite quickly) at long TC
LLR: -2.95 (-2.94,2.94) [0.00,6.00]
Total: 8451 W: 1386 L: 1448 D: 5617

But when ran with a conclusive 40K fixed games at 60 secs it proved
almost equivalent to original one.

ELO: 1.08 +-2.0 (95%) LOS: 85.8%
Total: 40000 W: 6739 L: 6615 D: 26646

bench: 3884003
2013-09-16 08:57:37 +02:00
Reuven Peleg 21cbfafc03 Code style at passed pawn eval
No functional change.
2013-09-15 21:49:06 +02:00
Reuven Peleg d3947b2f3e Nicer operator declerations
No functional change.
2013-09-15 21:45:18 +02:00
Kojirion a71209868b Use pre-increment also for native types
Now that we use pre-increment on enums, it
make sense, for code style uniformity, to
swith to pre-increment also for native types,
although there is no speed difference.

No functional change.
2013-09-15 09:17:21 +02:00
Marco Costalba 7a1ff6d8ff Fix operator++ definition
ENABLE_OPERATORS_ON has incorrect definitions of
post-increment and post-decrement operators.

In particularly the returned value is the variable
already incremented/decremented, while instead they
should return the variable _before_ inc/dec.

This has no real effect because are only used in loops
and where the returned value is never used, neverthless
it is wrong. The fix would be to copy the variable to a
dummy, then inc/dec the variable, then return the dummy.

So instead, rename to pre-increment that can be implemented
without the dummy, actually the current implementation
it is already the correct pre-increment, with the only change
to return a reference (an l-value) and not a copy, so
to properly mimic the pre-increment on native integers.

Spotted by Kojirion.

No functional change.
2013-09-15 09:09:06 +02:00
Marco Costalba 82f6779c2e Don't blunder under extreme time pressure
We always attempt to keep at least this emergencyBaseTime
at clock. But if available time is very low it means that
we will force ourself to play immediately to satisfy the
emergencyBaseTime constrain and so leading to blunders.

Patch is good at short and very short TC (15secs and 5secs respectively)
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 26590 W: 5426 L: 5245 D: 15919

LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 5767 W: 1397 L: 1268 D: 3102

Instead seems has no influence at longer TC (60 secs)
LLR: -2.96 (-2.94,2.94) [0.00,6.00]
Total: 79862 W: 13623 L: 13339 D: 52900

So it is committed to have a broader testing but is
to be consider still EXPERIMENTAL and can be reverted
easily.

No functional change.
2013-09-15 07:59:09 +02:00
Marco Costalba 3abccdc82d Move classify_leaf() to c'tor in bitbases
No functional change.
2013-09-14 13:08:37 +02:00
Marco Costalba 8d6d0223bf Small touches to bitbase.cpp
Inspired by Lucas's code:

https://github.com/lucasart/chess/blob/master/src/kpk.cc

No functional change.
2013-09-14 11:19:12 +02:00
Reuven Peleg fc17d0de77 Increase passed bonus for having more pieces
Passed both short TC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 36463 W: 7575 L: 7365 D: 21523

And long TC:
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 2953 W: 564 L: 446 D: 1943

bench: 3846852
2013-09-13 18:08:22 +02:00
Marco Costalba 27f2ce8f6e Revert "Move draw by material check"
Possible regression

bench: 4554579
2013-09-12 08:44:11 +02:00
Marco Costalba 45b0aea875 Revert "Fix random moves when time < 10ms"
Possible regression.

No functional change.
2013-09-12 08:38:19 +02:00
Marco Costalba 4803d5772c Extend checks more when below alpha
Passed both short TC:
LLR: 2.97 (-2.94,2.94) [-1.50,4.50]
Total: 8739 W: 1830 L: 1698 D: 5211

And long TC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 6716 W: 1238 L: 1101 D: 4377

bench: 4554576
2013-09-11 19:15:28 +02:00
Uri Blass 738c5595ad Extend checks more in losing positions
Passed both short TC:
LLR: 2.98 (-2.94,2.94) [-1.50,4.50]
Total: 3974 W: 860 L: 741 D: 2373

And long TC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 16807 W: 2917 L: 2733 D: 11157

bench: 3767999
2013-09-11 09:15:47 +02:00
Reuven Peleg bebd6e16f6 Simplify unstoppable pawns evaluation
No functional change
2013-09-10 23:02:05 +02:00
Marco Costalba 49e110c52b Fix random moves when time < 10ms
In case we have less then 10ms to think as soon as
we wake up the timer, it immediately fires and calls
check_time() where due to condition:

elapsed > TimeMgr.maximum_time() - 2 * TimerResolution

the stop flag is set and search returns immediately, without
actually search anything.

Here the somewhat hacky fix is to start the timer after
at least one iteration as been completed.

No functional change.
2013-09-10 21:23:20 +02:00
Reuven Peleg 4d90aeb0ab More readable space mask
No functional change.
2013-09-10 19:18:10 +02:00
homoSapiensSapiens 03cd049c68 Change condition to use relative rank
No functional change
2013-09-10 00:47:31 +02:00
Marco Costalba 6ab8b9b6c6 Fix some comments in position.cpp
No functional change.
2013-09-08 06:28:53 -07:00
Marco Costalba 490f67a3f8 Move draw by material check
It is more natural to test this case among
others material distributions.

No functional change.
2013-09-08 06:11:35 -07:00
Marco Costalba 0515ad0fb0 Remove unreachable values in mobility table
The possible maximum mobility cardinality (plus one in case of
zero squares available) is:

- Knights: max. 8  squares -> max. 9  entries
- Bishops: max. 13 squares -> max. 14 entries
- Rooks:   max. 14 squares -> max. 15 entries
- Queen:   max. 27 squares -> max. 28 entries

So remove the extra entries in the table.

Spotted by Dariusz Orzechowski.

No functional change.
2013-09-07 18:25:24 +02:00
Marco Costalba bf51db2526 Fix warning: double to float truncation
MSVC 2013 says:
warning C4305: '*=' : truncation from 'double' to 'float'

No functional change.
2013-09-07 12:30:44 +02:00
Lucas Braesch 59702aca0d Singular extension at 8 plies also for PV nodes
Passed both short TC:
LLR: 2.96 (-2.94,2.94)
Total: 11451 W: 2455 L: 2282 D: 6714

And long TC
LLR: 2.96 (-2.94,2.94)
Total: 15813 W: 2907 L: 2723 D: 10183

bench: 3864419
2013-09-07 09:34:22 +02:00
Lucas Braesch c86eee3918 Union of 2 changes
Union of

- LMR >= 3 plies from Gary tests.stockfishchess.org/tests/view/522522960ebc595d328fcafd

- allows() tweak from Reuven tests.stockfishchess.org/tests/view/5225fa1c0ebc595d328fcb53

Both passed Step I and failed Step II.

Instead this union passed both short TC:
LLR: 2.95 (-2.94,2.94)
Total: 14525 W: 3063 L: 2874 D: 8588

And long TC
LLR: 2.94 (-2.94,2.94)
Total: 31075 W: 5566 L: 5308 D: 20201

bench: 4238160
2013-09-07 09:25:45 +02:00
Lucas Braesch 10b53e1c5e Do not prune useless checks in QS
Passed both SPRT tests in "simplification mode", so with
elo0: -3.00 alpha: 0.05 elo1: 3.00 beta: 0.05

Short TC:
LLR: 2.96 (-2.94,2.94)
Total: 6243 W: 1302 L: 1195 D: 3746

Long TC
LLR: 2.96 (-2.94,2.94)
Total: 22972 W: 4124 L: 4020 D: 14828

bench: 4633330
2013-09-05 18:50:16 +02:00
Marco Costalba a30d3571ca Revert "Fix check for bishop pair in material imbalance"
Idea is sound but implementation is partial. Ryan and Joona noticed that
    we leave an hole in material table. Also we got another report by an user
    of an odd behaviour. Namely, if you start stockfish and from the prompt
    give 'bench' you get 3453941, then if you run again bench you get 3453940.

    The reason is that two different positions with the same number of pieces,
    but one with a bishop pair and another without have the same material key.
    But after Eelco patch also different material imbalance and this yields
    to this issue.

    Restesting at long TC shows the patch does not really contribute at
    ELO improvement. Actually patch failed at long TC.

    LLR: -2.97 (-2.94,2.94)
    Total: 23109 W: 4104 L: 4092 D: 14913

    So revert.

    bench: 3453945
2013-09-05 06:34:48 +02:00
Reuven Peleg 457ac26de5 Rewrite backward pawn detection
Use the new backmost_sq() instead of a loop.

No functional change.
2013-09-03 20:11:00 +02:00
Eelco de Groot 679c2ea227 Fix check for bishop pair in material imbalance
Prefer pos.bishop_pair() to pos.count<BISHOP>(WHITE) > 1
because the first checks that the two bishops are on
different color squares.

Although the change seems to kick in only in very rare cases,
quite surprisingly it was able to pass SPRT test at short TC.

LLR: 2.95 (-2.94,2.94)
Total: 39818 W: 8174 L: 7956 D: 23688

bench: 3453941
2013-09-03 19:40:34 +02:00
Marco Costalba 9ff594c3a9 Rewrite KBBKN endgame
This was thought to be a draw but the bishops generally win. However,
it takes up to 66 moves. The position in the diagram was thought to be
a draw for over one hundred years, but tablebases show that White wins
in 45 moves. All of the long wins go through this type of semi-fortress
position. It takes several moves to force Black out of the temporary
fortress in the corner; then precise play with the bishops prevents Black
from forming the temporary fortress in another corner (Nunn 1995:265ff).

Before computer analysis, Speelman listed this position as unresolved,
but "probably a draw" (Speelman 1981:109).

bench: 3453945
2013-09-02 11:03:01 +02:00
Marco Costalba 849b089a63 Don't use lpthread for Android
Thanks to Peter Osterlund for the feedback.

No functional change.
2013-09-01 13:48:09 -07:00
Marco Costalba aee404f532 Improve ARM compatibility
STANDALONE-TOOLCHAIN.html in Android NDK says:

It is recommended to use the -mthumb compiler flag to force the generation
of 16-bit Thumb-1 instructions (the default being 32-bit ARM ones).

If you want to target the 'armeabi-v7a' ABI, you will need ensure that the
following two flags are being used:

  CFLAGS='-march=armv7-a -mfloat-abi=softfp'

Note: The first flag enables Thumb-2 instructions, and the second one
      enables H/W FPU instructions while ensuring that floating-point
      parameters are passed in core registers, which is critical for
      ABI compatibility. Do *not* use these flags separately!

Thanks to Peter Osterlund for pointout this doc and for showing me
an example Makefile to follow.

No functional change.
2013-09-01 09:18:37 -07:00
Uri Blass 0915f85895 Union of 2 changes that failed with good score
This is a union of 2 changes:

A tweak of recaptures limit from Joona Kiiski
http://tests.stockfishchess.org/tests/view/52166d7c0ebc59319a242400

and a tweak of move count pruning from Leonid Pechenik
http://tests.stockfishchess.org/tests/view/5217c7e60ebc59319a242456

The set passed both short TC at 30+0.05
LLR: 2.96 (-2.94,2.94)
Total: 18936 W: 3723 L: 3566 D: 11647

And the usual long TC at 60+0.05
LLR: 2.95 (-2.94,2.94)
Total: 48962 W: 8837 L: 8487 D: 31638

bench: 3453945
2013-09-01 08:07:21 -07:00
Marco Costalba 5e8bc6ac2a Assorted clean up in endgames
No functional change.
2013-09-01 07:39:04 -07:00
Marco Costalba 3e4dcaa06e Fix a bogus assert in allows()
Becuase castle is coded as "king captures the rook"
the to_sq(move), A1/8 or H1/8 is empty after the move,
leading to assert assert(p != NO_PIECE) in color_of().

Teach allows() asserts about castle and fix the crash.

Bug reported by Ryan Takker and tracked down by Tom Vijlbrief.

No functional change.
2013-08-30 16:42:18 +02:00
Marco Costalba 14f47c8ac6 Use frontmost_sq() and backmost_sq helpers
Should easier to read than the lsb() / msb() low
level functions.

No functional change.
2013-08-30 16:22:22 +02:00
Gary Linscott 5d90c149b5 Enable LMR for dangerous moves
Passed both short TC
LLR: 2.96 (-2.94,2.94)
Total: 5598 W: 1250 L: 1125 D: 3223

And long TC
LLR: 2.97 (-2.94,2.94)
Total: 16441 W: 3102 L: 2912 D: 10427

bench: 4620975
2013-08-29 23:02:18 +02:00
homoSapiensSapiens 4b9e338541 Bonus for rook behind a passed
If our rook is behind a passed pawn, all
squares are defended.

One of the longest tests to pass !

Passed both short TC
LLR: 2.97 (-2.94,2.94)
Total: 44560 W: 9518 L: 9281 D: 25761

And long TC
LLR: 2.96 (-2.94,2.94)
Total: 61348 W: 11618 L: 11192 D: 38538

bench: 3787694
2013-08-29 22:53:21 +02:00
homoSapiensSapiens a0cf424cfc Replace hardcoded 128 by constant
No functional change.
2013-08-29 13:59:49 +02:00
Gary Linscott aecdbfc4a0 Add lsb() overload
Helper to find least significant bit relative to
the given color.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-08-28 19:50:11 +02:00
Uri Blass e6482b7d97 Time management: move faster if PV is stable
Move faster but compensate by allocating more
time when the best move changes.

Passed short TC at 15+0.05
LLR: 2.93 (-2.94,2.94)
Total: 13895 W: 3030 L: 2882 D: 798

Long TC at 60+0.05
LLR: 2.96 (-2.94,2.94)
Total: 9266 W: 1777 L: 1624 D: 5865

At time increment 30+0.5
LLR: 2.96 (-2.94,2.94)
Total: 6703 W: 1238 L: 1134 D: 4331

And at fixed game number, longer TC 120+0.05
ELO: 5.17 +-2.8 (95%) LOS: 100.0%
Total: 19306 W: 3378 L: 3091 D: 12837

bench: 4728533
2013-08-26 10:29:58 -07:00
homoSapiensSapiens 6e6c5b6103 Simplify chain detection
No functional change.
2013-08-25 18:36:49 +02:00
homoSapiensSapiens 04fd7efdfa Simplify kf definition in shelter_storm()
No functional change.
2013-08-25 18:31:47 +02:00
Chris Caino 5e331f9618 Fix KBPK bug
With

position fen 7k/8/8/8/8/7P/6K1/7B w - - 0 1
go depth 25

The evaluation at depth 22 is not draw as it should be. The reason is that
when search reaches the position 8/6kP/8/8/8/3B4/6K1/8 w - - 0 1 if white plays
h8R or h8N then we get a position that is a "KNOWN_WIN" and is _not_ a check, so
futility pruning in qsearch kicks in and black may think that it is "futile"
to reply Kxh8 since, according to the logic of the code, it cannot raise the score
back towards a draw.

bench: 4728533
2013-08-24 10:08:03 +02:00
homoSapiensSapiens b9f5d1c6ff Simplify condition for backward pawn
No functional changes.
2013-08-22 14:39:08 +02:00
Marco Costalba c4533e0d94 Retire redundant endgames
The case of two lone kings on the board is already considered
by the "No pawns" scaling factor rules in material.cpp as is
KBK and KNK.

Moreover we had a small leak in endgames map because for
KK endgame it happens white and black material keys are the
same (both equal to zero), so when adding the black endgame in
Endgames::add() we were overwriting the already exsisting
white one, leading to a memory leak found by Valgrind.

So remove the endgames althogheter and rely on scaling
to correctly set the endgames value to a draw.

No functional change.
2013-08-22 13:13:06 +02:00
Joona Kiiski f39cf1b008 Use null move when depth >= 2 plies
Passed both short TC:
LLR: 2.96 (-2.94,2.94)
Total: 23725 W: 5031 L: 4855 D: 13839

And long TC:
LLR: 2.96 (-2.94,2.94)
Total: 15730 W: 2939 L: 2754 D: 10037

bench: 4729333
2013-08-22 09:06:48 +02:00
Marco Costalba c6baefb79d Restore development version
No functional change.
2013-08-21 08:41:47 +02:00
32 changed files with 960 additions and 1251 deletions
+20 -6
View File
@@ -165,6 +165,16 @@ ifeq ($(ARCH),osx-ppc-32)
sse = no sse = no
endif endif
ifeq ($(ARCH),linux-ppc-64)
arch = ppc64
os = any
bits = 64
prefetch = no
bsfq = no
popcnt = no
sse = no
endif
ifeq ($(ARCH),osx-x86-64) ifeq ($(ARCH),osx-x86-64)
arch = x86_64 arch = x86_64
os = osx os = osx
@@ -243,7 +253,7 @@ ifeq ($(comp),mingw)
endif endif
ifeq ($(comp),icc) ifeq ($(comp),icc)
CXXFLAGS += -wd383,981,1418,1419,1476,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif endif
ifeq ($(comp),clang) ifeq ($(comp),clang)
@@ -263,9 +273,12 @@ endif
### On mingw use Windows threads, otherwise POSIX ### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw) ifneq ($(comp),mingw)
# Haiku has pthreads in its libroot, so only link it in on other platforms # On Android Bionic's C library comes with its own pthread implementation bundled in
ifneq ($(UNAME),Haiku) ifneq ($(arch),armv7)
LDFLAGS += -lpthread # Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
LDFLAGS += -lpthread
endif
endif endif
endif endif
@@ -296,7 +309,7 @@ ifeq ($(optimize),yes)
endif endif
ifeq ($(arch),armv7) ifeq ($(arch),armv7)
CXXFLAGS += -fno-gcse CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
endif endif
endif endif
@@ -308,7 +321,7 @@ ifeq ($(optimize),yes)
ifeq ($(os),osx) ifeq ($(os),osx)
CXXFLAGS += -fast -mdynamic-no-pic CXXFLAGS += -fast -mdynamic-no-pic
else else
CXXFLAGS += -O3 CXXFLAGS += -fast
endif endif
endif endif
@@ -394,6 +407,7 @@ help:
@echo "x86-64-modern > x86 64-bit with popcnt support" @echo "x86-64-modern > x86 64-bit with popcnt support"
@echo "x86-32 > x86 32-bit with SSE support" @echo "x86-32 > x86 32-bit with SSE support"
@echo "x86-32-old > x86 32-bit fall back for old hardware" @echo "x86-32-old > x86 32-bit fall back for old hardware"
@echo "linux-ppc-64 > PPC-Linux 64 bit"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit" @echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit" @echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit" @echo "osx-x86-64 > x86-Mac OS X 64 bit"
+18 -4
View File
@@ -47,7 +47,21 @@ static const char* Defaults[] = {
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26" "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1",
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1"
}; };
@@ -68,7 +82,7 @@ void benchmark(const Position& current, istream& is) {
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = (is >> token) ? token : "32"; string ttSize = (is >> token) ? token : "32";
string threads = (is >> token) ? token : "1"; string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "12"; string limit = (is >> token) ? token : "13";
string fenFile = (is >> token) ? token : "default"; string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth"; string limitType = (is >> token) ? token : "depth";
@@ -89,7 +103,7 @@ void benchmark(const Position& current, istream& is) {
limits.depth = atoi(limit.c_str()); limits.depth = atoi(limit.c_str());
if (fenFile == "default") if (fenFile == "default")
fens.assign(Defaults, Defaults + 16); fens.assign(Defaults, Defaults + 30);
else if (fenFile == "current") else if (fenFile == "current")
fens.push_back(current.fen()); fens.push_back(current.fen());
@@ -116,7 +130,7 @@ void benchmark(const Position& current, istream& is) {
Search::StateStackPtr st; Search::StateStackPtr st;
Time::point elapsed = Time::now(); Time::point elapsed = Time::now();
for (size_t i = 0; i < fens.size(); i++) for (size_t i = 0; i < fens.size(); ++i)
{ {
Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
+20 -22
View File
@@ -55,8 +55,8 @@ namespace {
struct KPKPosition { struct KPKPosition {
operator Result() const { return res; } KPKPosition(unsigned idx);
Result classify_leaf(unsigned idx); operator Result() const { return result; }
Result classify(const std::vector<KPKPosition>& db) Result classify(const std::vector<KPKPosition>& db)
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); } { return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
@@ -65,7 +65,7 @@ namespace {
Color us; Color us;
Square bksq, wksq, psq; Square bksq, wksq, psq;
Result res; Result result;
}; };
} // namespace } // namespace
@@ -83,21 +83,21 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
void Bitbases::init_kpk() { void Bitbases::init_kpk() {
unsigned idx, repeat = 1; unsigned idx, repeat = 1;
std::vector<KPKPosition> db(IndexMax); std::vector<KPKPosition> db;
db.reserve(IndexMax);
// Initialize db with known win / draw positions // Initialize db with known win / draw positions
for (idx = 0; idx < IndexMax; idx++) for (idx = 0; idx < IndexMax; ++idx)
db[idx].classify_leaf(idx); db.push_back(KPKPosition(idx));
// Iterate through the positions until no more of the unknown positions can be // Iterate through the positions until no more of the unknown positions can be
// changed to either wins or draws (15 cycles needed). // changed to either wins or draws (15 cycles needed).
while (repeat) while (repeat)
for (repeat = idx = 0; idx < IndexMax; idx++) for (repeat = idx = 0; idx < IndexMax; ++idx)
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN) repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
repeat = 1;
// Map 32 results into one KPKBitbase[] entry // Map 32 results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax; idx++) for (idx = 0; idx < IndexMax; ++idx)
if (db[idx] == WIN) if (db[idx] == WIN)
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
} }
@@ -105,34 +105,32 @@ void Bitbases::init_kpk() {
namespace { namespace {
Result KPKPosition::classify_leaf(unsigned idx) { KPKPosition::KPKPosition(unsigned idx) {
wksq = Square((idx >> 0) & 0x3F); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15)); psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
result = UNKNOWN;
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq if ( square_distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq
|| (StepAttacksBB[KING][wksq] & bksq)
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
return res = INVALID; result = INVALID;
if (us == WHITE) else if (us == WHITE)
{ {
// Immediate win if pawn can be promoted without getting captured // Immediate win if pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7 if ( rank_of(psq) == RANK_7
&& wksq != psq + DELTA_N && wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1 && ( square_distance(bksq, psq + DELTA_N) > 1
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
return res = WIN; result = WIN;
} }
// Immediate draw if is stalemate or king captures undefended pawn // Immediate draw if is stalemate or king captures undefended pawn
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) || (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
return res = DRAW; result = DRAW;
return res = UNKNOWN;
} }
template<Color Us> template<Color Us>
@@ -162,14 +160,14 @@ namespace {
Square s = psq + DELTA_N; Square s = psq + DELTA_N;
r |= db[index(BLACK, bksq, wksq, s)]; // Single push r |= db[index(BLACK, bksq, wksq, s)]; // Single push
if (rank_of(s) == RANK_3 && s != wksq && s != bksq) if (rank_of(psq) == RANK_2 && s != wksq && s != bksq)
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
} }
if (Us == WHITE) if (Us == WHITE)
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW; return result = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
else else
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN; return result = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
} }
} // namespace } // namespace
+26 -22
View File
@@ -45,6 +45,7 @@ Bitboard AdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8]; Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
@@ -132,11 +133,11 @@ void Bitboards::print(Bitboard b) {
sync_cout; sync_cout;
for (Rank rank = RANK_8; rank >= RANK_1; rank--) for (Rank rank = RANK_8; rank >= RANK_1; --rank)
{ {
std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File file = FILE_A; file <= FILE_H; file++) for (File file = FILE_A; file <= FILE_H; ++file)
std::cout << "| " << (b & (file | rank) ? "X " : " "); std::cout << "| " << (b & (file | rank) ? "X " : " ");
std::cout << "|\n"; std::cout << "|\n";
@@ -150,41 +151,41 @@ void Bitboards::print(Bitboard b) {
void Bitboards::init() { void Bitboards::init() {
for (int k = 0, i = 0; i < 8; i++) for (int k = 0, i = 0; i < 8; ++i)
while (k < (2 << i)) while (k < (2 << i))
MS1BTable[k++] = i; MS1BTable[k++] = i;
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; ++i)
BSFTable[bsf_index(1ULL << i)] = Square(i); BSFTable[bsf_index(1ULL << i)] = Square(i);
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = 1ULL << s; SquareBB[s] = 1ULL << s;
FileBB[FILE_A] = FileABB; FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB; RankBB[RANK_1] = Rank1BB;
for (int i = 1; i < 8; i++) for (int i = 1; i < 8; ++i)
{ {
FileBB[i] = FileBB[i - 1] << 1; FileBB[i] = FileBB[i - 1] << 1;
RankBB[i] = RankBB[i - 1] << 8; RankBB[i] = RankBB[i - 1] << 8;
} }
for (File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; ++f)
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
for (Rank r = RANK_1; r < RANK_8; r++) for (Rank r = RANK_1; r < RANK_8; ++r)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s]; PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{ {
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
if (s1 != s2) if (s1 != s2)
@@ -194,10 +195,10 @@ void Bitboards::init() {
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int k = 0; steps[pt][k]; k++) for (int k = 0; steps[pt][k]; ++k)
{ {
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]); Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
@@ -211,20 +212,23 @@ void Bitboards::init() {
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>); init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>); init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0); PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0); PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (PseudoAttacks[QUEEN][s1] & s2) if (PseudoAttacks[QUEEN][s1] & s2)
{ {
Square delta = (s2 - s1) / square_distance(s1, s2); Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta) for (Square s = s1 + delta; s != s2; s += delta)
BetweenBB[s1][s2] |= s; BetweenBB[s1][s2] |= s;
PieceType pt = (PseudoAttacks[BISHOP][s1] & s2) ? BISHOP : ROOK;
LineBB[s1][s2] = (PseudoAttacks[pt][s1] & PseudoAttacks[pt][s2]) | s1 | s2;
} }
} }
@@ -235,7 +239,7 @@ namespace {
Bitboard attack = 0; Bitboard attack = 0;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; ++i)
for (Square s = sq + deltas[i]; for (Square s = sq + deltas[i];
is_ok(s) && square_distance(s, s - deltas[i]) == 1; is_ok(s) && square_distance(s, s - deltas[i]) == 1;
s += deltas[i]) s += deltas[i])
@@ -281,7 +285,7 @@ namespace {
// attacks[s] is a pointer to the beginning of the attacks table for square 's' // attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table; attacks[SQ_A1] = table;
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
// Board edges are not considered in the relevant occupancies // Board edges are not considered in the relevant occupancies
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
@@ -322,7 +326,7 @@ namespace {
// looks up the correct sliding attack in the attacks[s] database. // looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side // Note that we build up the database for square 's' as a side
// effect of verifying the magic. // effect of verifying the magic.
for (i = 0; i < size; i++) for (i = 0; i < size; ++i)
{ {
Bitboard& attack = attacks[s][index(s, occupancy[i])]; Bitboard& attack = attacks[s][index(s, occupancy[i])];
+10 -4
View File
@@ -74,6 +74,7 @@ extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8]; extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
@@ -222,12 +223,11 @@ inline Bitboard squares_of_color(Square s) {
} }
/// squares_aligned() returns true if the squares s1, s2 and s3 are aligned /// aligned() returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line. /// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline bool aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) return LineBB[s1][s2] & s3;
& ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
} }
@@ -319,4 +319,10 @@ extern Square pop_lsb(Bitboard* b);
#endif #endif
/// frontmost_sq() and backmost_sq() find the square corresponding to the
/// most/least advanced bit relative to the given color.
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED
+2 -2
View File
@@ -361,7 +361,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) { template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
n = 0; n = 0;
for (size_t i = 0; i < sizeof(T); i++) for (size_t i = 0; i < sizeof(T); ++i)
n = T((n << 8) + ifstream::get()); n = T((n << 8) + ifstream::get());
return *this; return *this;
@@ -413,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 // 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 // high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen. // with lower score. Note that first entry is always chosen.
if ( (sum && rkiss.rand<unsigned>() % sum < e.count) if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best)) || (pickBest && e.count == best))
move = Move(e.move); move = Move(e.move);
} }
+308 -387
View File
File diff suppressed because it is too large Load Diff
+4 -6
View File
@@ -33,9 +33,6 @@ enum EndgameType {
// Evaluation functions // Evaluation functions
KK, // K vs K
KBK, // KB vs K
KNK, // KN vs K
KNNK, // KNN vs K KNNK, // KNN vs K
KXK, // Generic "mate lone king" eval KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
@@ -55,6 +52,7 @@ enum EndgameType {
KBPsK, // KB+pawns vs K KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR KRPKR, // KRP vs KR
KRPKB, // KRP vs KB
KRPPKRP, // KRPP vs KRP KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king KPsK, // King and pawns vs king
KBPKB, // KBP vs KB KBPKB, // KBP vs KB
@@ -88,12 +86,12 @@ struct EndgameBase {
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type> template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
struct Endgame : public EndgameBase<T> { struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {} explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
Color color() const { return strongerSide; } Color color() const { return strongSide; }
T operator()(const Position&) const; T operator()(const Position&) const;
private: private:
Color strongerSide, weakerSide; const Color strongSide, weakSide;
}; };
+144 -289
View File
@@ -32,7 +32,7 @@
namespace { namespace {
enum ExtendedPieceType { // Used for tracing enum ExtendedPieceType { // Used for tracing
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL
}; };
namespace Tracing { namespace Tracing {
@@ -83,6 +83,8 @@ namespace {
// king is on g8 and there's a white knight on g5, this knight adds // king is on g8 and there's a white knight on g5, this knight adds
// 2 to kingAdjacentZoneAttacksCount[BLACK]. // 2 to kingAdjacentZoneAttacksCount[BLACK].
int kingAdjacentZoneAttacksCount[COLOR_NB]; int kingAdjacentZoneAttacksCount[COLOR_NB];
Bitboard pinnedPieces[COLOR_NB];
}; };
// Evaluation grain size, must be a power of 2 // Evaluation grain size, must be a power of 2
@@ -114,16 +116,15 @@ namespace {
S( 37, 28), S( 42, 31), S(44, 33) }, S( 37, 28), S( 42, 31), S(44, 33) },
{ S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77), S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) }, S( 84, 79), S( 86, 81) },
{ S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119), S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
S( 35,122), S( 36,123), S(37,124), S(38,124) }, S( 35,122), S( 36,123), S(37,124) },
{ S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40), S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41), S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41), S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) }
S( 25, 41), S( 25, 41) }
}; };
// Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed // Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed
@@ -173,9 +174,11 @@ namespace {
const Score RookOpenFile = make_score(43, 21); const Score RookOpenFile = make_score(43, 21);
const Score RookSemiopenFile = make_score(19, 10); const Score RookSemiopenFile = make_score(19, 10);
const Score BishopPawns = make_score( 8, 12); const Score BishopPawns = make_score( 8, 12);
const Score KnightPawns = make_score( 8, 4);
const Score MinorBehindPawn = make_score(16, 0); const Score MinorBehindPawn = make_score(16, 0);
const Score UndefendedMinor = make_score(25, 10); const Score UndefendedMinor = make_score(25, 10);
const Score TrappedRook = make_score(90, 0); const Score TrappedRook = make_score(90, 0);
const Score Unstoppable = make_score( 0, 20);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // 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 // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
@@ -187,12 +190,8 @@ namespace {
// based on how many squares inside this area are safe and available for // based on how many squares inside this area are safe and available for
// friendly minor pieces. // friendly minor pieces.
const Bitboard SpaceMask[] = { const Bitboard SpaceMask[] = {
(1ULL << SQ_C2) | (1ULL << SQ_D2) | (1ULL << SQ_E2) | (1ULL << SQ_F2) | (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB),
(1ULL << SQ_C3) | (1ULL << SQ_D3) | (1ULL << SQ_E3) | (1ULL << SQ_F3) | (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
(1ULL << SQ_C4) | (1ULL << SQ_D4) | (1ULL << SQ_E4) | (1ULL << SQ_F4),
(1ULL << SQ_C7) | (1ULL << SQ_D7) | (1ULL << SQ_E7) | (1ULL << SQ_F7) |
(1ULL << SQ_C6) | (1ULL << SQ_D6) | (1ULL << SQ_E6) | (1ULL << SQ_F6) |
(1ULL << SQ_C5) | (1ULL << SQ_D5) | (1ULL << SQ_E5) | (1ULL << SQ_F5)
}; };
// King danger constants and variables. The king danger scores are taken // King danger constants and variables. The king danger scores are taken
@@ -204,12 +203,12 @@ namespace {
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
// Bonuses for enemy's safe checks // Bonuses for enemy's safe checks
const int QueenContactCheck = 6; const int QueenContactCheck = 24;
const int RookContactCheck = 4; const int RookContactCheck = 16;
const int QueenCheck = 3; const int QueenCheck = 12;
const int RookCheck = 2; const int RookCheck = 8;
const int BishopCheck = 1; const int BishopCheck = 2;
const int KnightCheck = 1; const int KnightCheck = 3;
// KingExposed[Square] contains penalties based on the position of the // KingExposed[Square] contains penalties based on the position of the
// defending king, indexed by king's square (from white's point of view). // defending king, indexed by king's square (from white's point of view).
@@ -230,16 +229,16 @@ namespace {
// Function prototypes // Function prototypes
template<bool Trace> template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin); Value do_evaluate(const Position& pos);
template<Color Us> template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei); void init_eval_info(const Position& pos, EvalInfo& ei);
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility);
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]); Score evaluate_king(const Position& pos, const EvalInfo& ei);
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei); Score evaluate_threats(const Position& pos, const EvalInfo& ei);
@@ -250,7 +249,7 @@ namespace {
template<Color Us> template<Color Us>
int evaluate_space(const Position& pos, const EvalInfo& ei); int evaluate_space(const Position& pos, const EvalInfo& ei);
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei); Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei);
Value interpolate(const Score& v, Phase ph, ScaleFactor sf); Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
Score apply_weight(Score v, Score w); Score apply_weight(Score v, Score w);
@@ -265,8 +264,8 @@ namespace Eval {
/// values, an endgame score and a middle game score, and interpolates /// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material. /// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) { Value evaluate(const Position& pos) {
return do_evaluate<false>(pos, margin); return do_evaluate<false>(pos);
} }
@@ -293,7 +292,7 @@ namespace Eval {
const int MaxSlope = 30; const int MaxSlope = 30;
const int Peak = 1280; const int Peak = 1280;
for (int t = 0, i = 1; i < 100; i++) for (int t = 0, i = 1; i < 100; ++i)
{ {
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
@@ -308,19 +307,14 @@ namespace Eval {
namespace { namespace {
template<bool Trace> template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) { Value do_evaluate(const Position& pos) {
assert(!pos.checkers()); assert(!pos.checkers());
EvalInfo ei; EvalInfo ei;
Value margins[COLOR_NB]; Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
Score score, mobilityWhite, mobilityBlack;
Thread* th = pos.this_thread(); Thread* th = pos.this_thread();
// margins[] store the uncertainty estimation of position's evaluation
// that typically is used by the search for pruning decisions.
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Initialize score by reading the incrementally updated scores included // Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables) and adding // in the position object (material + piece square tables) and adding
// Tempo bonus. Score is computed from the point of view of white. // Tempo bonus. Score is computed from the point of view of white.
@@ -333,10 +327,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
// configuration, call it and return. // configuration, call it and return.
if (ei.mi->specialized_eval_exists()) if (ei.mi->specialized_eval_exists())
{
margin = VALUE_ZERO;
return ei.mi->evaluate(pos); return ei.mi->evaluate(pos);
}
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = Pawns::probe(pos, th->pawnsTable); ei.pi = Pawns::probe(pos, th->pawnsTable);
@@ -347,15 +338,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
init_eval_info<BLACK>(pos, ei); init_eval_info<BLACK>(pos, ei);
// Evaluate pieces and mobility // Evaluate pieces and mobility
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite) score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobility)
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack); - evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobility);
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack // Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation. // information when computing the king safety evaluation.
score += evaluate_king<WHITE, Trace>(pos, ei, margins) score += evaluate_king<WHITE, Trace>(pos, ei)
- evaluate_king<BLACK, Trace>(pos, ei, margins); - evaluate_king<BLACK, Trace>(pos, ei);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE, Trace>(pos, ei) score += evaluate_threats<WHITE, Trace>(pos, ei)
@@ -365,9 +356,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei) score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei); - evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, check whether exists any unstoppable passed pawn // If one side has only a king, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns(pos, ei); score += evaluate_unstoppable_pawns(pos, WHITE, ei)
- evaluate_unstoppable_pawns(pos, BLACK, ei);
// Evaluate space for both sides, only in middle-game. // Evaluate space for both sides, only in middle-game.
if (ei.mi->space_weight()) if (ei.mi->space_weight())
@@ -401,7 +393,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
sf = ScaleFactor(50); sf = ScaleFactor(50);
} }
margin = margins[pos.side_to_move()];
Value v = interpolate(score, ei.mi->game_phase(), sf); Value v = interpolate(score, ei.mi->game_phase(), sf);
// In case of tracing add all single evaluation contributions for both white and black // In case of tracing add all single evaluation contributions for both white and black
@@ -410,14 +401,11 @@ Value do_evaluate(const Position& pos, Value& margin) {
Tracing::add(PST, pos.psq_score()); Tracing::add(PST, pos.psq_score());
Tracing::add(IMBALANCE, ei.mi->material_value()); Tracing::add(IMBALANCE, ei.mi->material_value());
Tracing::add(PAWN, ei.pi->pawns_value()); Tracing::add(PAWN, ei.pi->pawns_value());
Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei); Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei); Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
Tracing::add(TOTAL, score); Tracing::add(TOTAL, score);
Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE]) Tracing::stream << "\nScaling: " << std::noshowpos
<< ", Black: " << to_cp(margins[BLACK])
<< "\nScaling: " << std::noshowpos
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, " << std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
<< std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * " << std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * "
<< std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n" << std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
@@ -437,6 +425,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them)); Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
@@ -447,7 +437,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0; ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
} else }
else
ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0; ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
} }
@@ -474,14 +465,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
else else
bonus += bonus / 2; bonus += bonus / 2;
} }
return make_score(bonus, bonus); return make_score(bonus, bonus);
} }
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color // evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
template<PieceType Piece, Color Us, bool Trace> template<PieceType Piece, Color Us, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard mobilityArea) {
Bitboard b; Bitboard b;
Square s; Square s;
@@ -499,13 +491,16 @@ Value do_evaluate(const Position& pos, Value& margin) {
: Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN)) : Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
: pos.attacks_from<Piece>(s); : pos.attacks_from<Piece>(s);
if (ei.pinnedPieces[Us] & s)
b &= LineBB[pos.king_square(Us)][s];
ei.attackedBy[Us][Piece] |= b; ei.attackedBy[Us][Piece] |= b;
if (b & ei.kingRing[Them]) if (b & ei.kingRing[Them])
{ {
ei.kingAttackersCount[Us]++; ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]); Bitboard bb = b & ei.attackedBy[Them][KING];
if (bb) if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb); ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
} }
@@ -513,7 +508,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea) int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea); : popcount<Full >(b & mobilityArea);
mobility += MobilityBonus[Piece][mob]; mobility[Us] += MobilityBonus[Piece][mob];
// Decrease score if we are attacked by an enemy pawn. Remaining part // 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. // of threat evaluation must be done later when we have full attack info.
@@ -531,6 +526,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
if (Piece == BISHOP) if (Piece == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
// Penalty for knight when there are few enemy pawns
if (Piece == KNIGHT)
score -= KnightPawns * std::max(5 - pos.count<PAWN>(Them), 0);
if (Piece == BISHOP || Piece == KNIGHT) if (Piece == BISHOP || Piece == KNIGHT)
{ {
// Bishop and knight outposts squares // Bishop and knight outposts squares
@@ -587,9 +586,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
const enum Piece P = make_piece(Us, PAWN); const enum Piece P = make_piece(Us, PAWN);
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == P) if (pos.piece_on(s + d) == P)
score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2 : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
: TrappedBishopA1H1; : TrappedBishopA1H1;
} }
} }
@@ -600,82 +599,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// evaluate_threats<>() assigns bonuses according to the type of attacking piece // evaluate_pieces_of_color() assigns bonuses and penalties to all the
// and the type of attacked one.
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, undefendedMinors, weakEnemies;
Score score = SCORE_ZERO;
// Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
& ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors)
score += UndefendedMinor;
// Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
// Add bonus according to type of attacked enemy piece and to the
// type of attacking piece, from knights to queens. Kings are not
// considered because are already handled in king evaluation.
if (weakEnemies)
for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
{
b = ei.attackedBy[Us][pt1] & weakEnemies;
if (b)
for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
if (b & pos.pieces(pt2))
score += Threat[pt1][pt2];
}
if (Trace)
Tracing::scores[Us][THREAT] = score;
return score;
}
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// pieces of a given color. // pieces of a given color.
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING)); const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING));
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea); Score score = evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea)
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea); + evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea)
score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea); + evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea)
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea); + evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares // Sum up all attacked squares (updated in evaluate_pieces)
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
if (Trace) if (Trace)
Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]); Tracing::scores[Us][MOBILITY] = apply_weight(mobility[Us], Weights[Mobility]);
return score; return score;
} }
// evaluate_king<>() assigns bonuses and penalties to a king of a given color // evaluate_king() assigns bonuses and penalties to a king of a given color
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) { Score evaluate_king(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -686,15 +640,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
// King shelter and enemy pawns storm // King shelter and enemy pawns storm
Score score = ei.pi->king_safety<Us>(pos, ksq); Score score = ei.pi->king_safety<Us>(pos, ksq);
// King safety. This is quite complicated, and is almost certainly far // Main king safety evaluation
// from optimally tuned.
if ( ei.kingAttackersCount[Them] >= 2 if ( ei.kingAttackersCount[Them] >= 2
&& ei.kingAdjacentZoneAttacksCount[Them]) && ei.kingAdjacentZoneAttacksCount[Them])
{ {
// Find the attacked squares around the king which has no defenders // Find the attacked squares around the king which has no defenders
// apart from the king itself // apart from the king itself
undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING]; undefended = ei.attackedBy[Them][ALL_PIECES]
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] & ei.attackedBy[Us][KING]
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN]); | ei.attackedBy[Us][QUEEN]);
@@ -703,7 +657,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// number and types of the enemy's attacking pieces, the number of // number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the // attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter. // king, and the quality of the pawn shelter.
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended)) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ KingExposed[relative_square(Us, ksq)] + KingExposed[relative_square(Us, ksq)]
- mg_value(score) / 32; - mg_value(score) / 32;
@@ -770,12 +724,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
attackUnits = std::min(99, std::max(0, attackUnits)); attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDanger[] // Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation. Set also margins[] // array and subtract the score from evaluation.
// 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 -= KingDanger[Us == Search::RootColor][attackUnits]; score -= KingDanger[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]);
} }
if (Trace) if (Trace)
@@ -785,7 +735,50 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// evaluate_passed_pawns<>() evaluates the passed pawns of the given color // evaluate_threats() assigns bonuses according to the type of attacking piece
// and the type of attacked one.
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, undefendedMinors, weakEnemies;
Score score = SCORE_ZERO;
// Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
& ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors)
score += UndefendedMinor;
// Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
// Add bonus according to type of attacked enemy piece and to the
// type of attacking piece, from knights to queens. Kings are not
// considered because are already handled in king evaluation.
if (weakEnemies)
for (PieceType pt1 = KNIGHT; pt1 < KING; ++pt1)
{
b = ei.attackedBy[Us][pt1] & weakEnemies;
if (b)
for (PieceType pt2 = PAWN; pt2 < KING; ++pt2)
if (b & pos.pieces(pt2))
score += Threat[pt1][pt2];
}
if (Trace)
Tracing::scores[Us][THREAT] = score;
return score;
}
// evaluate_passed_pawns() evaluates the passed pawns of the given color
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
@@ -801,7 +794,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
assert(pos.pawn_is_passed(Us, s)); assert(pos.pawn_passed(Us, s));
int r = int(relative_rank(Us, s) - RANK_2); int r = int(relative_rank(Us, s) - RANK_2);
int rr = r * (r - 1); int rr = r * (r - 1);
@@ -815,18 +808,17 @@ Value do_evaluate(const Position& pos, Value& margin) {
Square blockSq = s + pawn_push(Us); Square blockSq = s + pawn_push(Us);
// Adjust bonus based on kings proximity // Adjust bonus based on kings proximity
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr); ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr)
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr); - Value(square_distance(pos.king_square(Us ), blockSq) * 2 * rr);
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1)) if (relative_rank(Us, blockSq) != RANK_8)
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
// If the pawn is free to advance, increase bonus // If the pawn is free to advance, increase bonus
if (pos.is_empty(blockSq)) if (pos.empty(blockSq))
{ {
squaresToQueen = forward_bb(Us, s); squaresToQueen = forward_bb(Us, s);
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
// If there is an enemy rook or queen attacking the pawn from behind, // 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 // add all X-ray attacks by the rook or queen. Otherwise consider only
@@ -837,6 +829,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
else else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
defendedSquares = squaresToQueen;
else
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
// If there aren't enemy attacks huge bonus, a bit smaller if at // If there aren't enemy attacks huge bonus, a bit smaller if at
// least block square is not attacked, otherwise smallest bonus. // least block square is not attacked, otherwise smallest bonus.
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3; int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
@@ -872,9 +870,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
{ {
if (pos.non_pawn_material(Them) <= KnightValueMg) if (pos.non_pawn_material(Them) <= KnightValueMg)
ebonus += ebonus / 4; ebonus += ebonus / 4;
else if (pos.pieces(Them, ROOK, QUEEN)) else if (pos.pieces(Them, ROOK, QUEEN))
ebonus -= ebonus / 4; ebonus -= ebonus / 4;
} }
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
ebonus += ebonus / 4;
score += make_score(mbonus, ebonus); score += make_score(mbonus, ebonus);
} }
@@ -887,164 +890,18 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite // evaluate_unstoppable_pawns() scores the most advanced among the passed and
// conservative and returns a winning score only when we are very sure that the pawn is winning. // candidate pawns. In case opponent has no pieces but pawns, this is somewhat
// related to the possibility pawns are unstoppable.
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) { Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
Bitboard b, b2, blockers, supporters, queeningPath, candidates; Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us);
Square s, blockSq, queeningSquare;
Color c, winnerSide, loserSide;
bool pathDefended, opposed;
int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d;
int pliesToQueen[] = { 256, 256 };
// Step 1. Hunt for unstoppable passed pawns. If we find at least one, if (!b || pos.non_pawn_material(~us))
// record how many plies are required for promotion.
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(~c))
continue;
b = ei.pi->passed_pawns(c);
while (b)
{
s = pop_lsb(&b);
queeningSquare = relative_square(c, file_of(s) | RANK_8);
queeningPath = forward_bb(c, s);
// Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
// Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers.
assert(!pos.checkers());
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition
movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
pliesToGo = 2 * movesToGo - int(c == pos.side_to_move());
pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo);
}
}
// Step 2. If either side cannot promote at least three plies before the other side then situation
// becomes too complex and we give up. Otherwise we determine the possibly "winning side"
if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3)
return SCORE_ZERO; return SCORE_ZERO;
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
loserSide = ~winnerSide;
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
b = candidates = pos.pieces(loserSide, PAWN);
while (b)
{
s = pop_lsb(&b);
// Compute plies from queening
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Check if (without even considering any obstacles) we're too far away or doubled
if ( pliesToQueen[winnerSide] + 3 <= pliesToGo
|| (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN)))
candidates ^= s;
}
// If any candidate is already a passed pawn it _may_ promote in time. We give up.
if (candidates & ei.pi->passed_pawns(loserSide))
return SCORE_ZERO;
// Step 4. Check new passed pawn creation through king capturing and pawn sacrifices
b = candidates;
while (b)
{
s = pop_lsb(&b);
sacptg = blockersCount = 0;
minKingDist = kingptg = 256;
// Compute plies from queening
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Generate list of blocking pawns and supporters
supporters = adjacent_files_bb(file_of(s)) & candidates;
opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN);
assert(blockers);
// How many plies does it take to remove all the blocking pawns?
while (blockers)
{
blockSq = pop_lsb(&blockers);
movesToGo = 256;
// Check pawns that can give support to overcome obstacle, for instance
// black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
if (!opposed)
{
b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide)));
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
movesToGo = std::min(movesToGo, d);
}
}
// Check pawns that can be sacrificed against the blocking pawn
b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s);
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
movesToGo = std::min(movesToGo, d);
}
// If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
// it's not a real obstacle and we have nothing to add to pliesToGo.
if (movesToGo <= 0)
continue;
// Plies needed to sacrifice against all the blocking pawns
sacptg += movesToGo * 2;
blockersCount++;
// Plies needed for the king to capture all the blocking pawns
d = square_distance(pos.king_square(loserSide), blockSq);
minKingDist = std::min(minKingDist, d);
kingptg = (minKingDist + blockersCount) * 2;
}
// Check if pawn sacrifice plan _may_ save the day
if (pliesToQueen[winnerSide] + 3 > pliesToGo + sacptg)
return SCORE_ZERO;
// Check if king capture plan _may_ save the day (contains some false positives)
if (pliesToQueen[winnerSide] + 3 > pliesToGo + kingptg)
return SCORE_ZERO;
}
// Winning pawn is unstoppable and will promote as first, return big score
Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]);
return winnerSide == WHITE ? score : -score;
} }
@@ -1089,9 +946,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE); assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME); assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; int e = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128; int r = (mg_value(v) * int(ph) + e * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME;
return Value((result / GrainSize) * GrainSize); // Sign independent return Value((r / GrainSize) * GrainSize); // Sign independent
} }
// apply_weight() weights score v by score w trying to prevent overflow // apply_weight() weights score v by score w trying to prevent overflow
@@ -1129,7 +986,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score bScore = scores[BLACK][idx]; Score bScore = scores[BLACK][idx];
switch (idx) { switch (idx) {
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL: case PST: case IMBALANCE: case PAWN: case TOTAL:
stream << std::setw(20) << name << " | --- --- | --- --- | " stream << std::setw(20) << name << " | --- --- | --- --- | "
<< std::setw(6) << to_cp(mg_value(wScore)) << " " << std::setw(6) << to_cp(mg_value(wScore)) << " "
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n"; << std::setw(6) << to_cp(eg_value(wScore)) << " \n";
@@ -1152,8 +1009,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score)); std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
Value margin; do_evaluate<true>(pos);
do_evaluate<true>(pos, margin);
std::string totals = stream.str(); std::string totals = stream.str();
stream.str(""); stream.str("");
@@ -1173,7 +1029,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
row("King safety", KING); row("King safety", KING);
row("Threats", THREAT); row("Threats", THREAT);
row("Passed pawns", PASSED); row("Passed pawns", PASSED);
row("Unstoppable pawns", UNSTOPPABLE);
row("Space", SPACE); row("Space", SPACE);
stream << "---------------------+-------------+-------------+---------------\n"; stream << "---------------------+-------------+-------------+---------------\n";
+1 -1
View File
@@ -27,7 +27,7 @@ class Position;
namespace Eval { namespace Eval {
extern void init(); extern void init();
extern Value evaluate(const Position& pos, Value& margin); extern Value evaluate(const Position& pos);
extern std::string trace(const Position& pos); extern std::string trace(const Position& pos);
} }
+2 -1
View File
@@ -37,13 +37,14 @@ int main(int argc, char* argv[]) {
Position::init(); Position::init();
Bitbases::init_kpk(); Bitbases::init_kpk();
Search::init(); Search::init();
Pawns::init();
Eval::init(); Eval::init();
Threads.init(); Threads.init();
TT.set_size(Options["Hash"]); TT.set_size(Options["Hash"]);
std::string args; std::string args;
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; ++i)
args += std::string(argv[i]) + " "; args += std::string(argv[i]) + " ";
UCI::loop(args); UCI::loop(args);
+15 -22
View File
@@ -35,31 +35,29 @@ namespace {
const int NoPawnsSF[4] = { 6, 12, 32 }; const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters // Polynomial material balance parameters
const Value RedundantQueen = Value(320);
const Value RedundantRook = Value(554);
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -52 };
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 7 }, // Bishop pair { 0 }, // Bishop pair
{ 39, 2 }, // Pawn { 39, 2 }, // Pawn
{ 35, 271, -4 }, // Knight { 35, 271, -4 }, // Knight
{ 7, 105, 4, 7 }, // Bishop { 0, 105, 4, 0 }, // Bishop
{ -27, -2, 46, 100, 56 }, // Rook { -27, -2, 46, 100, -141 }, // Rook
{ 58, 29, 83, 148, -3, -25 } // Queen { 58, 29, 83, 148, -163, 0 } // Queen
}; };
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
// THEIR PIECES // THEIR PIECES
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
{ 41 }, // Bishop pair { 0 }, // Bishop pair
{ 37, 41 }, // Pawn { 37, 0 }, // Pawn
{ 10, 62, 41 }, // Knight OUR PIECES { 10, 62, 0 }, // Knight OUR PIECES
{ 57, 64, 39, 41 }, // Bishop { 57, 64, 39, 0 }, // Bishop
{ 50, 40, 23, -22, 41 }, // Rook { 50, 40, 23, -22, 0 }, // Rook
{ 106, 101, 3, 151, 171, 41 } // Queen { 106, 101, 3, 151, 171, 0 } // Queen
}; };
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions accessed direcly and not through
@@ -106,14 +104,8 @@ namespace {
int pt1, pt2, pc, v; int pt1, pt2, pc, v;
int value = 0; 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 -= RedundantRook * (pieceCount[Us][ROOK] - 1)
+ RedundantQueen * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{ {
pc = pieceCount[Us][pt1]; pc = pieceCount[Us][pt1];
if (!pc) if (!pc)
@@ -121,7 +113,7 @@ namespace {
v = LinearCoefficients[pt1]; v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
@@ -240,7 +232,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
} }
} }
// No pawns makes it difficult to win, even with a material advantage // No pawns makes it difficult to win, even with a material advantage. This
// catches some trivial draws like KK, KBK and KNK
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg) if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
{ {
e->factor[WHITE] = (uint8_t) e->factor[WHITE] = (uint8_t)
+4 -4
View File
@@ -28,7 +28,7 @@ using namespace std;
/// Version number. If Version is left empty, then compile date, in the /// Version number. If Version is left empty, then compile date, in the
/// format DD-MM-YY, is shown in engine_info. /// format DD-MM-YY, is shown in engine_info.
static const string Version = "4"; static const string Version = "DD";
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This
@@ -63,9 +63,9 @@ const string engine_info(bool to_uci) {
static uint64_t hits[2], means[2]; static uint64_t hits[2], means[2];
void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; } void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_mean_of(int v) { means[0]++; means[1] += v; } void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
void dbg_print() { void dbg_print() {
@@ -75,7 +75,7 @@ void dbg_print() {
if (means[0]) if (means[0])
cerr << "Total " << means[0] << " Mean " cerr << "Total " << means[0] << " Mean "
<< (float)means[1] / means[0] << endl; << (double)means[1] / means[0] << endl;
} }
+9 -26
View File
@@ -56,13 +56,13 @@ namespace {
// Because we generate only legal castling moves we need to verify that // Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker. // when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
return mlist; return mlist;
(mlist++)->move = make<CASTLE>(kfrom, rfrom); (mlist++)->move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) if (Checks && !pos.gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--; --mlist;
return mlist; return mlist;
} }
@@ -359,31 +359,14 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
// evasions so to skip known illegal moves avoiding useless legality check later. // evasions so to skip known illegal moves avoiding useless legality check later.
do do
{ {
checkersCnt++; ++checkersCnt;
checksq = pop_lsb(&b); checksq = pop_lsb(&b);
assert(color_of(pos.piece_on(checksq)) == ~us); assert(color_of(pos.piece_on(checksq)) == ~us);
switch (type_of(pos.piece_on(checksq))) if (type_of(pos.piece_on(checksq)) > KNIGHT) // A slider
{ sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
case QUEEN:
// If queen and king are far or not on a diagonal line we can safely
// remove all the squares attacked in the other direction becuase are
// not reachable by the king anyway.
if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq))
sliderAttacks |= PseudoAttacks[QUEEN][checksq];
// Otherwise we need to use real rook attacks to check if king is safe
// to move in the other direction. For example: king in B2, queen in A1
// a knight in B1, and we can safely move to C1.
else
sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
}
} while (b); } while (b);
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
@@ -407,17 +390,17 @@ template<>
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) { ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
ExtMove *end, *cur = mlist; ExtMove *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
Square ksq = pos.king_square(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move());
end = pos.checkers() ? generate<EVASIONS>(pos, mlist) end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
: generate<NON_EVASIONS>(pos, mlist); : generate<NON_EVASIONS>(pos, mlist);
while (cur != end) while (cur != end)
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
&& !pos.pl_move_is_legal(cur->move, pinned)) && !pos.legal(cur->move, pinned))
cur->move = (--end)->move; cur->move = (--end)->move;
else else
cur++; ++cur;
return end; return end;
} }
+1 -1
View File
@@ -42,7 +42,7 @@ template<GenType T>
struct MoveList { struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; } explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
void operator++() { cur++; } void operator++() { ++cur; }
Move operator*() const { return cur->move; } Move operator*() const { return cur->move; }
size_t size() const { return last - mlist; } size_t size() const { return last - mlist; }
bool contains(Move m) const { bool contains(Move m) const {
+14 -14
View File
@@ -86,7 +86,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
else else
stage = MAIN_SEARCH; stage = MAIN_SEARCH;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
@@ -108,7 +108,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
// Skip TT move if is not a capture or a promotion, this avoids qsearch // Skip TT move if is not a capture or a promotion, this avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases // tree explosion due to a possible perpetual check or similar rare cases
// when TT table is full. // when TT table is full.
if (ttm && !pos.is_capture_or_promotion(ttm)) if (ttm && !pos.capture_or_promotion(ttm))
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
else else
@@ -118,7 +118,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
@@ -131,9 +131,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Piece
// In ProbCut we generate only captures better than parent's captured piece // 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); ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) if (ttMove && (!pos.capture(ttMove) || pos.see(ttMove) <= captureThreshold))
ttMove = MOVE_NONE; ttMove = MOVE_NONE;
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
@@ -163,7 +163,7 @@ void MovePicker::score<CAPTURES>() {
{ {
m = it->move; 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)); - type_of(pos.moved_piece(m));
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
@@ -181,7 +181,7 @@ void MovePicker::score<QUIETS>() {
for (ExtMove* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = history[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.moved_piece(m)][to_sq(m)];
} }
} }
@@ -199,11 +199,11 @@ void MovePicker::score<EVASIONS>() {
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
it->score = seeScore - HistoryStats::Max; // At the bottom it->score = seeScore - HistoryStats::Max; // At the bottom
else if (pos.is_capture(m)) else if (pos.capture(m))
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)) + HistoryStats::Max; - type_of(pos.moved_piece(m)) + HistoryStats::Max;
else else
it->score = history[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.moved_piece(m)][to_sq(m)];
} }
} }
@@ -231,7 +231,7 @@ void MovePicker::generate_next() {
killers[2].move = killers[3].move = MOVE_NONE; killers[2].move = killers[3].move = MOVE_NONE;
// Be sure countermoves are different from killers // Be sure countermoves are different from killers
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; ++i)
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move) if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i]; (end++)->move = countermoves[i];
@@ -299,7 +299,7 @@ Move MovePicker::next_move<false>() {
switch (stage) { switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
cur++; ++cur;
return ttMove; return ttMove;
case CAPTURES_S1: case CAPTURES_S1:
@@ -317,9 +317,9 @@ Move MovePicker::next_move<false>() {
case KILLERS_S1: case KILLERS_S1:
move = (cur++)->move; move = (cur++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move) && pos.pseudo_legal(move)
&& move != ttMove && move != ttMove
&& !pos.is_capture(move)) && !pos.capture(move))
return move; return move;
break; break;
+5 -5
View File
@@ -133,7 +133,7 @@ const string move_to_san(Position& pos, Move m) {
while (b) while (b)
{ {
Move move = make_move(pop_lsb(&b), to); Move move = make_move(pop_lsb(&b), to);
if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) if (!pos.legal(move, pos.pinned_pieces(pos.side_to_move())))
others ^= from_sq(move); others ^= from_sq(move);
} }
@@ -149,10 +149,10 @@ const string move_to_san(Position& pos, Move m) {
san += square_to_string(from); san += square_to_string(from);
} }
} }
else if (pos.is_capture(m)) else if (pos.capture(m))
san = file_to_char(file_of(from)); san = file_to_char(file_of(from));
if (pos.is_capture(m)) if (pos.capture(m))
san += 'x'; san += 'x';
san += square_to_string(to); san += square_to_string(to);
@@ -161,7 +161,7 @@ const string move_to_san(Position& pos, Move m) {
san += string("=") + PieceToChar[WHITE][promotion_type(m)]; san += string("=") + PieceToChar[WHITE][promotion_type(m)];
} }
if (pos.move_gives_check(m, CheckInfo(pos))) if (pos.gives_check(m, CheckInfo(pos)))
{ {
StateInfo st; StateInfo st;
pos.do_move(m, st); pos.do_move(m, st);
@@ -207,7 +207,7 @@ static string score_to_string(Value v) {
s << "-#" << (VALUE_MATE + v) / 2; s << "-#" << (VALUE_MATE + v) / 2;
else else
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg; s << setprecision(2) << fixed << showpos << double(v) / PawnValueMg;
return s.str(); return s.str();
} }
+55 -48
View File
@@ -30,38 +30,32 @@ namespace {
#define V Value #define V Value
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by file
const Score Doubled[2][FILE_NB] = { const Score Doubled[FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file // Isolated pawn penalty by opposed flag and file
const Score Isolated[2][FILE_NB] = { const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), { 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(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
// Backward pawn penalty by opposed flag and file // Backward pawn penalty by opposed flag and file
const Score Backward[2][FILE_NB] = { const Score Backward[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), { 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(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Pawn chain membership bonus by file // Pawn chain membership bonus by file and rank (initialized by formula)
const Score ChainMember[FILE_NB] = { Score ChainMember[FILE_NB][RANK_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 // Candidate passed pawn bonus by rank
const Score CandidatePassed[RANK_NB] = { const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0) };
};
// Weakness of our pawn shelter in front of the king indexed by [rank] // Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] = const Value ShelterWeakness[RANK_NB] =
@@ -72,10 +66,10 @@ namespace {
const Value StormDanger[3][RANK_NB] = { const Value StormDanger[3][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) }, { V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) }, { V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V( 64), V(25), V(13) }}; { V( 0), V( 0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont. // in front of the king and no enemy pawn on the horizon.
const Value MaxSafetyBonus = V(263); const Value MaxSafetyBonus = V(263);
#undef S #undef S
@@ -92,7 +86,6 @@ namespace {
Bitboard b; Bitboard b;
Square s; Square s;
File f; File f;
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us); const Square* pl = pos.list<PAWN>(Us);
@@ -100,7 +93,7 @@ namespace {
Bitboard ourPawns = pos.pieces(Us, PAWN); Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = 0; e->passedPawns[Us] = e->candidatePawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE; e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF; e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns); e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
@@ -113,13 +106,12 @@ namespace {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = file_of(s); f = file_of(s);
r = rank_of(s);
// This file cannot be semi-open // This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection // Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); b = rank_bb(s) | rank_bb(s - pawn_push(Us));
// Flag the pawn as passed, isolated, doubled or member of a pawn // Flag the pawn as passed, isolated, doubled or member of a pawn
// chain (but not the backward one). // chain (but not the backward one).
@@ -129,35 +121,31 @@ namespace {
opposed = theirPawns & forward_bb(Us, s); opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s)); passed = !(theirPawns & passed_pawn_mask(Us, s));
// Test for backward pawn // Test for backward pawn.
backward = false;
// If the pawn is passed, isolated, or member of a pawn chain it cannot // 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 // be backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either. // or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain) if ( (passed | isolated | chain)
&& !(ourPawns & pawn_attack_span(Them, s)) || (ourPawns & pawn_attack_span(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
// pawn on adjacent files. We now check whether the pawn is // pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the adjacent // backward by looking in the forward direction on the adjacent
// files, and seeing whether we meet a friendly or an enemy pawn first. // files, and picking the closest pawn there.
b = pos.attacks_from<PAWN>(s, Us); b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
// Note that we are sure to find something because pawn is not passed // If we have an enemy pawn in the same or next rank, the pawn is
// nor isolated, so loop is potentially infinite, but it isn't. // backward because it cannot advance without being captured.
while (!(b & (ourPawns | theirPawns)))
b = shift_bb<Up>(b);
// 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 | shift_bb<Up>(b)) & theirPawns; backward = (b | shift_bb<Up>(b)) & theirPawns;
} }
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to // 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 // advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher or equal than the number of // pawn on adjacent files is higher or equal than the number of
// enemy pawns in the forward direction on the adjacent files. // enemy pawns in the forward direction on the adjacent files.
@@ -176,16 +164,21 @@ namespace {
value -= Isolated[opposed][f]; value -= Isolated[opposed][f];
if (doubled) if (doubled)
value -= Doubled[opposed][f]; value -= Doubled[f];
if (backward) if (backward)
value -= Backward[opposed][f]; value -= Backward[opposed][f];
if (chain) if (chain)
value += ChainMember[f]; value += ChainMember[f][relative_rank(Us, s)];
if (candidate) if (candidate)
{
value += CandidatePassed[relative_rank(Us, s)]; value += CandidatePassed[relative_rank(Us, s)];
if (!doubled)
e->candidatePawns[Us] |= s;
}
} }
return value; return value;
@@ -195,6 +188,22 @@ namespace {
namespace Pawns { namespace Pawns {
/// init() initializes some tables by formula instead of hard-code their values
void init() {
const int chainByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
int bonus;
for (Rank r = RANK_1; r < RANK_8; ++r)
for (File f = FILE_A; f <= FILE_H; ++f)
{
bonus = r * (r-1) * (r-2) + chainByFile[f] * (r/2 + 1);
ChainMember[f][r] = make_score(bonus, bonus);
}
}
/// probe() takes a position object as input, computes a Entry object, and returns /// 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 /// 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. /// to recompute everything when the same pawn structure occurs again.
@@ -226,18 +235,16 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
Bitboard ourPawns = b & pos.pieces(Us); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem; Rank rkUs, rkThem;
File kf = file_of(ksq); File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf; for (File f = kf - File(1); f <= kf + File(1); ++f)
for (int f = kf - 1; f <= kf + 1; f++)
{ {
b = ourPawns & FileBB[f]; b = ourPawns & file_bb(f);
rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
safety -= ShelterWeakness[rkUs]; safety -= ShelterWeakness[rkUs];
b = theirPawns & FileBB[f]; b = theirPawns & file_bb(f);
rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1; rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
} }
@@ -264,7 +271,7 @@ Score Entry::update_safety(const Position& pos, Square ksq) {
Value bonus = shelter_storm<Us>(pos, ksq); Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castle if is bigger // If we can castle use the bonus after the castle if it is bigger
if (pos.can_castle(make_castle_right(Us, KING_SIDE))) if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1))); bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
+3
View File
@@ -37,6 +37,7 @@ struct Entry {
Score pawns_value() const { return value; } Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; }
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; } int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); } int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
int semiopen_on_side(Color c, File f, bool left) const { int semiopen_on_side(Color c, File f, bool left) const {
@@ -59,6 +60,7 @@ struct Entry {
Key key; Key key;
Bitboard passedPawns[COLOR_NB]; Bitboard passedPawns[COLOR_NB];
Bitboard candidatePawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
int minKPdistance[COLOR_NB]; int minKPdistance[COLOR_NB];
@@ -71,6 +73,7 @@ struct Entry {
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 16384> Table;
void init();
Entry* probe(const Position& pos, Table& entries); Entry* probe(const Position& pos, Table& entries);
} }
+86 -86
View File
@@ -98,7 +98,7 @@ CheckInfo::CheckInfo(const Position& pos) {
Color them = ~pos.side_to_move(); Color them = ~pos.side_to_move();
ksq = pos.king_square(them); ksq = pos.king_square(them);
pinned = pos.pinned_pieces(); pinned = pos.pinned_pieces(pos.side_to_move());
dcCandidates = pos.discovered_check_candidates(); dcCandidates = pos.discovered_check_candidates();
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them); checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
@@ -120,15 +120,15 @@ void Position::init() {
RKISS rk; RKISS rk;
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
Zobrist::psq[c][pt][s] = rk.rand<Key>(); Zobrist::psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; ++f)
Zobrist::enpassant[f] = rk.rand<Key>(); Zobrist::enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++) for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; ++cr)
{ {
Bitboard b = cr; Bitboard b = cr;
while (b) while (b)
@@ -141,14 +141,14 @@ void Position::init() {
Zobrist::side = rk.rand<Key>(); Zobrist::side = rk.rand<Key>();
Zobrist::exclusion = rk.rand<Key>(); Zobrist::exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; ++pt)
{ {
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][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++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
psq[WHITE][pt][ s] = (v + PSQT[pt][s]); psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]); psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
@@ -233,7 +233,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((p = PieceToChar.find(token)) != string::npos) else if ((p = PieceToChar.find(token)) != string::npos)
{ {
put_piece(sq, color_of(Piece(p)), type_of(Piece(p))); put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
sq++; ++sq;
} }
} }
@@ -255,10 +255,10 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
token = char(toupper(token)); token = char(toupper(token));
if (token == 'K') if (token == 'K')
for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {} for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; --rsq) {}
else if (token == 'Q') else if (token == 'Q')
for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {} for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {}
else if (token >= 'A' && token <= 'H') else if (token >= 'A' && token <= 'H')
rsq = File(token - 'A') | relative_rank(c, RANK_1); rsq = File(token - 'A') | relative_rank(c, RANK_1);
@@ -317,11 +317,11 @@ void Position::set_castle_right(Color c, Square rfrom) {
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++) for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
if (s != kfrom && s != rfrom) if (s != kfrom && s != rfrom)
castlePath[c][cs] |= s; castlePath[c][cs] |= s;
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++) for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
if (s != kfrom && s != rfrom) if (s != kfrom && s != rfrom)
castlePath[c][cs] |= s; castlePath[c][cs] |= s;
} }
@@ -334,18 +334,18 @@ const string Position::fen() const {
std::ostringstream ss; std::ostringstream ss;
for (Rank rank = RANK_8; rank >= RANK_1; rank--) for (Rank rank = RANK_8; rank >= RANK_1; --rank)
{ {
for (File file = FILE_A; file <= FILE_H; file++) for (File file = FILE_A; file <= FILE_H; ++file)
{ {
Square sq = file | rank; Square sq = file | rank;
if (is_empty(sq)) if (empty(sq))
{ {
int emptyCnt = 1; int emptyCnt = 1;
for ( ; file < FILE_H && is_empty(sq++); file++) for ( ; file < FILE_H && empty(++sq); ++file)
emptyCnt++; ++emptyCnt;
ss << emptyCnt; ss << emptyCnt;
} }
@@ -422,7 +422,7 @@ const string Position::pretty(Move move) const {
/// pieces, according to the call parameters. Pinned pieces protect our king, /// pieces, according to the call parameters. Pinned pieces protect our king,
/// discovery check pieces attack the enemy king. /// discovery check pieces attack the enemy king.
Bitboard Position::hidden_checkers(Square ksq, Color c) const { Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) const {
Bitboard b, pinners, result = 0; Bitboard b, pinners, result = 0;
@@ -435,7 +435,7 @@ Bitboard Position::hidden_checkers(Square ksq, Color c) const {
b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
if (!more_than_one(b)) if (!more_than_one(b))
result |= b & pieces(sideToMove); result |= b & pieces(toMove);
} }
return result; return result;
} }
@@ -472,17 +472,17 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
} }
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal /// Position::legal() tests whether a pseudo-legal move is legal
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { bool Position::legal(Move m, Bitboard pinned) const {
assert(is_ok(m)); assert(is_ok(m));
assert(pinned == pinned_pieces()); assert(pinned == pinned_pieces(sideToMove));
Color us = sideToMove; Color us = sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
assert(color_of(piece_moved(m)) == us); assert(color_of(moved_piece(m)) == us);
assert(piece_on(king_square(us)) == make_piece(us, KING)); assert(piece_on(king_square(us)) == make_piece(us, KING));
// En passant captures are a tricky special case. Because they are rather // En passant captures are a tricky special case. Because they are rather
@@ -497,7 +497,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
Bitboard b = (pieces() ^ from ^ capsq) | to; Bitboard b = (pieces() ^ from ^ capsq) | to;
assert(to == ep_square()); assert(to == ep_square());
assert(piece_moved(m) == make_piece(us, PAWN)); assert(moved_piece(m) == make_piece(us, PAWN));
assert(piece_on(capsq) == make_piece(them, PAWN)); assert(piece_on(capsq) == make_piece(them, PAWN));
assert(piece_on(to) == NO_PIECE); assert(piece_on(to) == NO_PIECE);
@@ -515,20 +515,20 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
// is moving along the ray towards or away from the king. // is moving along the ray towards or away from the king.
return !pinned return !pinned
|| !(pinned & from) || !(pinned & from)
|| squares_aligned(from, to_sq(m), king_square(us)); || aligned(from, to_sq(m), king_square(us));
} }
/// Position::is_pseudo_legal() takes a random move and tests whether the move /// Position::pseudo_legal() takes a random move and tests whether the move is
/// is pseudo legal. It is used to validate moves from TT that can be corrupted /// pseudo legal. It is used to validate moves from TT that can be corrupted
/// due to SMP concurrent access or hash position key aliasing. /// due to SMP concurrent access or hash position key aliasing.
bool Position::is_pseudo_legal(const Move m) const { bool Position::pseudo_legal(const Move m) const {
Color us = sideToMove; Color us = sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_moved(m); Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases // Use a slower but simpler function for uncommon cases
if (type_of(m) != NORMAL) if (type_of(m) != NORMAL)
@@ -581,7 +581,7 @@ bool Position::is_pseudo_legal(const Move m) const {
case DELTA_N: case DELTA_N:
case DELTA_S: case DELTA_S:
// Pawn push. The destination square must be empty. // Pawn push. The destination square must be empty.
if (!is_empty(to)) if (!empty(to))
return false; return false;
break; break;
@@ -590,8 +590,8 @@ bool Position::is_pseudo_legal(const Move m) const {
// rank, and both the destination square and the square between the // rank, and both the destination square and the square between the
// source and destination squares must be empty. // source and destination squares must be empty.
if ( rank_of(to) != RANK_4 if ( rank_of(to) != RANK_4
|| !is_empty(to) || !empty(to)
|| !is_empty(from + DELTA_N)) || !empty(from + DELTA_N))
return false; return false;
break; break;
@@ -600,8 +600,8 @@ bool Position::is_pseudo_legal(const Move m) const {
// rank, and both the destination square and the square between the // rank, and both the destination square and the square between the
// source and destination squares must be empty. // source and destination squares must be empty.
if ( rank_of(to) != RANK_5 if ( rank_of(to) != RANK_5
|| !is_empty(to) || !empty(to)
|| !is_empty(from + DELTA_S)) || !empty(from + DELTA_S))
return false; return false;
break; break;
@@ -639,11 +639,11 @@ bool Position::is_pseudo_legal(const Move m) const {
/// Position::move_gives_check() tests whether a pseudo-legal move gives a check /// Position::move_gives_check() tests whether a pseudo-legal move gives a check
bool Position::move_gives_check(Move m, const CheckInfo& ci) const { bool Position::gives_check(Move m, const CheckInfo& ci) const {
assert(is_ok(m)); assert(is_ok(m));
assert(ci.dcCandidates == discovered_check_candidates()); assert(ci.dcCandidates == discovered_check_candidates());
assert(color_of(piece_moved(m)) == sideToMove); assert(color_of(moved_piece(m)) == sideToMove);
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
@@ -658,7 +658,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
{ {
// For pawn and king moves we need to verify also direction // For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING) if ( (pt != PAWN && pt != KING)
|| !squares_aligned(from, to, king_square(~sideToMove))) || !aligned(from, to, king_square(~sideToMove)))
return true; return true;
} }
@@ -710,7 +710,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
void Position::do_move(Move m, StateInfo& newSt) { void Position::do_move(Move m, StateInfo& newSt) {
CheckInfo ci(*this); CheckInfo ci(*this);
do_move(m, newSt, ci, move_gives_check(m, ci)); do_move(m, newSt, ci, gives_check(m, ci));
} }
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
@@ -718,7 +718,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(is_ok(m)); assert(is_ok(m));
assert(&newSt != st); assert(&newSt != st);
nodes++; ++nodes;
Key k = st->key; Key k = st->key;
// Copy some fields of old state to our new StateInfo object except the ones // Copy some fields of old state to our new StateInfo object except the ones
@@ -734,9 +734,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Increment ply counters.In particular rule50 will be later reset it to zero // Increment ply counters.In particular rule50 will be later reset it to zero
// in case of a capture or a pawn move. // in case of a capture or a pawn move.
gamePly++; ++gamePly;
st->rule50++; ++st->rule50;
st->pliesFromNull++; ++st->pliesFromNull;
Color us = sideToMove; Color us = sideToMove;
Color them = ~us; Color them = ~us;
@@ -744,11 +744,11 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_on(from); Piece pc = piece_on(from);
PieceType pt = type_of(pc); PieceType pt = type_of(pc);
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(pc) == us); assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING); assert(captured != KING);
if (type_of(m) == CASTLE) if (type_of(m) == CASTLE)
{ {
@@ -758,7 +758,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE; captured = NO_PIECE_TYPE;
do_castle(from, to, rfrom, rto); do_castle(from, to, rfrom, rto);
@@ -766,13 +766,13 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
} }
if (capture) if (captured)
{ {
Square capsq = to; Square capsq = to;
// If the captured piece is a pawn, update pawn hash key, otherwise // If the captured piece is a pawn, update pawn hash key, otherwise
// update non-pawn material. // update non-pawn material.
if (capture == PAWN) if (captured == PAWN)
{ {
if (type_of(m) == ENPASSANT) if (type_of(m) == ENPASSANT)
{ {
@@ -790,18 +790,18 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
} }
else else
st->npMaterial[them] -= PieceValue[MG][capture]; st->npMaterial[them] -= PieceValue[MG][captured];
// Update board and piece lists // Update board and piece lists
remove_piece(capsq, them, capture); remove_piece(capsq, them, captured);
// Update material hash key and prefetch access to materialTable // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq]; k ^= Zobrist::psq[them][captured][capsq];
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]];
prefetch((char*)thisThread->materialTable[st->materialKey]); prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psq -= psq[them][capture][capsq]; st->psq -= psq[them][captured][capsq];
// Reset rule 50 counter // Reset rule 50 counter
st->rule50 = 0; st->rule50 = 0;
@@ -878,7 +878,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->psq += psq[us][pt][to] - psq[us][pt][from]; st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece // Set capture piece
st->capturedType = capture; st->capturedType = captured;
// Update the key with the final value // Update the key with the final value
st->key = k; st->key = k;
@@ -928,10 +928,10 @@ void Position::undo_move(Move m) {
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
PieceType pt = type_of(piece_on(to)); PieceType pt = type_of(piece_on(to));
PieceType capture = st->capturedType; PieceType captured = st->capturedType;
assert(is_empty(from) || type_of(m) == CASTLE); assert(empty(from) || type_of(m) == CASTLE);
assert(capture != KING); assert(captured != KING);
if (type_of(m) == PROMOTION) if (type_of(m) == PROMOTION)
{ {
@@ -952,14 +952,14 @@ void Position::undo_move(Move m) {
Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rfrom = to; // Castle is encoded as "king captures friendly rook"
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
capture = NO_PIECE_TYPE; captured = NO_PIECE_TYPE;
pt = KING; pt = KING;
do_castle(to, from, rto, rfrom); do_castle(to, from, rto, rfrom);
} }
else else
move_piece(to, from, us, pt); // Put the piece back at the source square move_piece(to, from, us, pt); // Put the piece back at the source square
if (capture) if (captured)
{ {
Square capsq = to; Square capsq = to;
@@ -973,12 +973,12 @@ void Position::undo_move(Move m) {
assert(piece_on(capsq) == NO_PIECE); assert(piece_on(capsq) == NO_PIECE);
} }
put_piece(capsq, them, capture); // Restore the captured piece put_piece(capsq, them, captured); // Restore the captured piece
} }
// Finally point our state pointer back to the previous state // Finally point our state pointer back to the previous state
st = st->previous; st = st->previous;
gamePly--; --gamePly;
assert(pos_is_ok()); assert(pos_is_ok());
} }
@@ -1019,7 +1019,7 @@ void Position::do_null_move(StateInfo& newSt) {
st->key ^= Zobrist::side; st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key)); prefetch((char*)TT.first_entry(st->key));
st->rule50++; ++st->rule50;
st->pliesFromNull = 0; st->pliesFromNull = 0;
sideToMove = ~sideToMove; sideToMove = ~sideToMove;
@@ -1049,7 +1049,7 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value // Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return // is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0. // here because king midgame value is set to 0.
if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))]) if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return 1; return 1;
return see(m); return see(m);
@@ -1067,7 +1067,7 @@ int Position::see(Move m, int asymmThreshold) const {
from = from_sq(m); from = from_sq(m);
to = to_sq(m); to = to_sq(m);
swapList[0] = PieceValue[MG][type_of(piece_on(to))]; swapList[0] = PieceValue[MG][piece_on(to)];
stm = color_of(piece_on(from)); stm = color_of(piece_on(from));
occupied = pieces() ^ from; occupied = pieces() ^ from;
@@ -1106,7 +1106,7 @@ int Position::see(Move m, int asymmThreshold) const {
// Add the new entry to the swap list // 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++; ++slIndex;
// Locate and remove the next least valuable attacker // Locate and remove the next least valuable attacker
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
@@ -1134,7 +1134,7 @@ int Position::see(Move m, int asymmThreshold) const {
// Having built the swap list, we negamax through it to find the best // 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. // achievable score from the point of view of the side to move.
while (--slIndex) while (--slIndex)
swapList[slIndex-1] = std::min(-swapList[slIndex], swapList[slIndex-1]); swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]);
return swapList[0]; return swapList[0];
} }
@@ -1149,9 +1149,9 @@ void Position::clear() {
startState.epSquare = SQ_NONE; startState.epSquare = SQ_NONE;
st = &startState; st = &startState;
for (int i = 0; i < 8; i++) for (int i = 0; i < PIECE_TYPE_NB; ++i)
for (int j = 0; j < 16; j++) for (int j = 0; j < 16; ++j)
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
} }
@@ -1210,9 +1210,9 @@ Key Position::compute_material_key() const {
Key k = 0; Key k = 0;
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (PieceType pt = PAWN; pt <= QUEEN; ++pt)
for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++) for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
k ^= Zobrist::psq[c][pt][cnt]; k ^= Zobrist::psq[c][pt][cnt];
return k; return k;
@@ -1223,6 +1223,7 @@ Key Position::compute_material_key() const {
/// game and the endgame. These functions are used to initialize the incremental /// game and the endgame. These functions are used to initialize the incremental
/// scores when a new position is set up, and to verify that the scores are correctly /// scores when a new position is set up, and to verify that the scores are correctly
/// updated by do_move and undo_move when the program is running in debug mode. /// updated by do_move and undo_move when the program is running in debug mode.
Score Position::compute_psq_score() const { Score Position::compute_psq_score() const {
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
@@ -1247,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO; Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
value += pieceCount[c][pt] * PieceValue[MG][pt]; value += pieceCount[c][pt] * PieceValue[MG][pt];
return value; return value;
@@ -1268,7 +1269,6 @@ bool Position::is_draw() const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;
// Draw by repetition?
int i = 4, e = std::min(st->rule50, st->pliesFromNull); int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (i <= e) if (i <= e)
@@ -1279,7 +1279,7 @@ bool Position::is_draw() const {
stp = stp->previous->previous; stp = stp->previous->previous;
if (stp->key == st->key) if (stp->key == st->key)
return true; return true; // Draw after first repetition
i += 2; i += 2;
@@ -1302,7 +1302,7 @@ void Position::flip() {
string f, token; string f, token;
std::stringstream ss(fen()); std::stringstream ss(fen());
for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement
{ {
std::getline(ss, token, rank > RANK_1 ? '/' : ' '); std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
f.insert(0, token + (f.empty() ? " " : "/")); f.insert(0, token + (f.empty() ? " " : "/"));
@@ -1366,9 +1366,9 @@ bool Position::pos_is_ok(int* failedStep) const {
{ {
int kingCount[COLOR_NB] = {}; int kingCount[COLOR_NB] = {};
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; ++s)
if (type_of(piece_on(s)) == KING) if (type_of(piece_on(s)) == KING)
kingCount[color_of(piece_on(s))]++; ++kingCount[color_of(piece_on(s))];
if (kingCount[0] != 1 || kingCount[1] != 1) if (kingCount[0] != 1 || kingCount[1] != 1)
return false; return false;
@@ -1393,8 +1393,8 @@ bool Position::pos_is_ok(int* failedStep) const {
return false; return false;
// Separate piece type bitboards must have empty intersections // Separate piece type bitboards must have empty intersections
for (PieceType p1 = PAWN; p1 <= KING; p1++) for (PieceType p1 = PAWN; p1 <= KING; ++p1)
for (PieceType p2 = PAWN; p2 <= KING; p2++) for (PieceType p2 = PAWN; p2 <= KING; ++p2)
if (p1 != p2 && (pieces(p1) & pieces(p2))) if (p1 != p2 && (pieces(p1) & pieces(p2)))
return false; return false;
} }
@@ -1420,21 +1420,21 @@ bool Position::pos_is_ok(int* failedStep) const {
return false; return false;
if ((*step)++, debugPieceCounts) if ((*step)++, debugPieceCounts)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; ++pt)
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt))) if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
return false; return false;
if ((*step)++, debugPieceList) if ((*step)++, debugPieceList)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = 0; i < pieceCount[c][pt]; i++) for (int i = 0; i < pieceCount[c][pt]; ++i)
if ( board[pieceList[c][pt][i]] != make_piece(c, pt) if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|| index[pieceList[c][pt][i]] != i) || index[pieceList[c][pt][i]] != i)
return false; return false;
if ((*step)++, debugCastleSquares) if ((*step)++, debugCastleSquares)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; ++c)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
{ {
CastleRight cr = make_castle_right(c, s); CastleRight cr = make_castle_right(c, s);
+37 -52
View File
@@ -43,10 +43,10 @@ struct CheckInfo {
}; };
/// The StateInfo struct stores information we need to restore a Position /// The StateInfo struct stores information needed to restore a Position
/// object to its previous state when we retract a move. Whenever a move /// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), a StateInfo object /// is made on the board (by calling Position::do_move), a StateInfo
/// must be passed as a parameter. /// object must be passed as a parameter.
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
@@ -67,27 +67,10 @@ struct StateInfo {
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
/// The position data structure. A position consists of the following data: /// The Position class stores the information regarding the board representation
/// /// like pieces, side to move, hash keys, castling info, etc. The most important
/// * For each piece type, a bitboard representing the squares occupied /// methods are do_move() and undo_move(), used by the search to update node info
/// by pieces of that type. /// when traversing the search tree.
/// * For each color, a bitboard representing the squares occupied by
/// pieces of that color.
/// * A bitboard of all occupied squares.
/// * A bitboard of all checking pieces.
/// * A 64-entry array of pieces, indexed by the squares of the board.
/// * The current side to move.
/// * Information about the castling rights for both sides.
/// * The initial files of the kings and both pairs of rooks. This is
/// used to implement the Chess960 castling rules.
/// * The en passant square (which is SQ_NONE if no en passant capture is
/// possible).
/// * The squares of the kings for both sides.
/// * Hash keys for the position itself, the current pawn structure, and
/// the current material situation.
/// * Hash keys for all previous positions in the game for detecting
/// repetition draws.
/// * A counter for detecting 50 move rule draws.
class Position { class Position {
public: public:
@@ -112,7 +95,7 @@ public:
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
Square king_square(Color c) const; Square king_square(Color c) const;
Square ep_square() const; Square ep_square() const;
bool is_empty(Square s) const; bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const; template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> const Square* list(Color c) const; template<PieceType Pt> const Square* list(Color c) const;
@@ -125,7 +108,7 @@ public:
// Checking // Checking
Bitboard checkers() const; Bitboard checkers() const;
Bitboard discovered_check_candidates() const; Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const; Bitboard pinned_pieces(Color toMove) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
@@ -136,20 +119,20 @@ public:
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves // Properties of moves
bool move_gives_check(Move m, const CheckInfo& ci) const; bool legal(Move m, Bitboard pinned) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const; bool pseudo_legal(const Move m) const;
bool is_pseudo_legal(const Move m) const; bool capture(Move m) const;
bool is_capture(Move m) const; bool capture_or_promotion(Move m) const;
bool is_capture_or_promotion(Move m) const; bool gives_check(Move m, const CheckInfo& ci) const;
bool is_passed_pawn_push(Move m) const; bool passed_pawn_push(Move m) const;
Piece piece_moved(Move m) const; Piece moved_piece(Move m) const;
PieceType captured_piece_type() const; PieceType captured_piece_type() const;
// Piece specific // Piece specific
bool pawn_is_passed(Color c, Square s) const; bool pawn_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const; bool pawn_on_7th(Color c) const;
bool opposite_bishops() const;
bool bishop_pair(Color c) const; bool bishop_pair(Color c) const;
bool opposite_bishops() const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
@@ -192,7 +175,7 @@ private:
// Helper functions // Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
Bitboard hidden_checkers(Square ksq, Color c) const; Bitboard hidden_checkers(Square ksq, Color c, Color toMove) const;
void put_piece(Square s, Color c, PieceType pt); void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt); void remove_piece(Square s, Color c, PieceType pt);
void move_piece(Square from, Square to, Color c, PieceType pt); void move_piece(Square from, Square to, Color c, PieceType pt);
@@ -239,11 +222,11 @@ inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Piece Position::piece_moved(Move m) const { inline Piece Position::moved_piece(Move m) const {
return board[from_sq(m)]; return board[from_sq(m)];
} }
inline bool Position::is_empty(Square s) const { inline bool Position::empty(Square s) const {
return board[s] == NO_PIECE; return board[s] == NO_PIECE;
} }
@@ -333,17 +316,23 @@ inline Bitboard Position::checkers() const {
} }
inline Bitboard Position::discovered_check_candidates() const { inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers(king_square(~sideToMove), sideToMove); return hidden_checkers(king_square(~sideToMove), sideToMove, sideToMove);
} }
inline Bitboard Position::pinned_pieces() const { inline Bitboard Position::pinned_pieces(Color toMove) const {
return hidden_checkers(king_square(sideToMove), ~sideToMove); return hidden_checkers(king_square(toMove), ~toMove, toMove);
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_passed(Color c, Square s) const {
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
} }
inline bool Position::passed_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN
&& pawn_passed(sideToMove, to_sq(m));
}
inline Key Position::key() const { inline Key Position::key() const {
return st->key; return st->key;
} }
@@ -364,12 +353,6 @@ inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c]; return st->npMaterial[c];
} }
inline bool Position::is_passed_pawn_push(Move m) const {
return type_of(piece_moved(m)) == PAWN
&& pawn_is_passed(sideToMove, to_sq(m));
}
inline int Position::game_ply() const { inline int Position::game_ply() const {
return gamePly; return gamePly;
} }
@@ -395,17 +378,17 @@ inline bool Position::is_chess960() const {
return chess960; return chess960;
} }
inline bool Position::is_capture_or_promotion(Move m) const { inline bool Position::capture_or_promotion(Move m) const {
assert(is_ok(m)); assert(is_ok(m));
return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m)); return type_of(m) ? type_of(m) != CASTLE : !empty(to_sq(m));
} }
inline bool Position::is_capture(Move m) const { inline bool Position::capture(Move m) const {
// Note that castle is coded as "king captures the rook" // Note that castle is coded as "king captures the rook"
assert(is_ok(m)); assert(is_ok(m));
return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; return (!empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
} }
inline PieceType Position::captured_piece_type() const { inline PieceType Position::captured_piece_type() const {
@@ -422,6 +405,7 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
byTypeBB[ALL_PIECES] |= s; byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s; byTypeBB[pt] |= s;
byColorBB[c] |= s; byColorBB[c] |= s;
pieceCount[c][ALL_PIECES]++;
index[s] = pieceCount[c][pt]++; index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s; pieceList[c][pt][index[s]] = s;
} }
@@ -450,6 +434,7 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
byTypeBB[pt] ^= s; byTypeBB[pt] ^= s;
byColorBB[c] ^= s; byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
pieceCount[c][ALL_PIECES]--;
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s]; index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare; pieceList[c][pt][index[lastSquare]] = lastSquare;
+9 -10
View File
@@ -43,7 +43,7 @@
class RKISS { class RKISS {
struct S { uint64_t a, b, c, d; } s; // Keep variables always together uint64_t a, b, c, d;
uint64_t rotate(uint64_t x, uint64_t k) const { uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k)); return (x << k) | (x >> (64 - k));
@@ -51,20 +51,19 @@ class RKISS {
uint64_t rand64() { uint64_t rand64() {
const uint64_t const uint64_t e = a - rotate(b, 7);
e = s.a - rotate(s.b, 7); a = b ^ rotate(c, 13);
s.a = s.b ^ rotate(s.c, 13); b = c + rotate(d, 37);
s.b = s.c + rotate(s.d, 37); c = d + e;
s.c = s.d + e; return d = e + a;
return s.d = e + s.a;
} }
public: public:
RKISS(int seed = 73) { RKISS(int seed = 73) {
s.a = 0xf1ea5eed; a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < seed; i++) // Scramble a few rounds for (int i = 0; i < seed; ++i) // Scramble a few rounds
rand64(); rand64();
} }
+118 -182
View File
@@ -19,6 +19,7 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cfloat>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
@@ -55,9 +56,6 @@ namespace {
// Set to true to force running with one thread. Used for debugging // Set to true to force running with one thread. Used for debugging
const bool FakeSplit = false; const bool FakeSplit = false;
// This is the minimum interval in msec between two check_time() calls
const int TimerResolution = 5;
// Different node types, used as template parameter // Different node types, used as template parameter
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
@@ -65,13 +63,10 @@ namespace {
inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); } inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
// Futility lookup tables (initialized at startup) and their access functions // Futility lookup tables (initialized at startup) and their access functions
Value FutilityMargins[16][64]; // [depth][moveNumber]
int FutilityMoveCounts[2][32]; // [improving][depth] int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d, int mn) { inline Value futility_margin(Depth d) {
return Value(100 * int(d));
return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)]
: 2 * VALUE_INFINITE;
} }
// Reduction lookup tables (initialized at startup) and their access function // Reduction lookup tables (initialized at startup) and their access function
@@ -84,7 +79,7 @@ namespace {
size_t PVSize, PVIdx; size_t PVSize, PVIdx;
TimeManager TimeMgr; TimeManager TimeMgr;
int BestMoveChanges; double BestMoveChanges;
Value DrawValue[COLOR_NB]; Value DrawValue[COLOR_NB];
HistoryStats History; HistoryStats History;
GainsStats Gains; GainsStats Gains;
@@ -99,7 +94,6 @@ namespace {
void id_loop(Position& pos); void id_loop(Position& pos);
Value value_to_tt(Value v, int ply); Value value_to_tt(Value v, int ply);
Value value_from_tt(Value v, int ply); Value value_from_tt(Value v, int ply);
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta);
bool allows(const Position& pos, Move first, Move second); bool allows(const Position& pos, Move first, Move second);
bool refutes(const Position& pos, Move first, Move second); bool refutes(const Position& pos, Move first, Move second);
string uci_pv(const Position& pos, int depth, Value alpha, Value beta); string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
@@ -132,7 +126,7 @@ void Search::init() {
int mc; // moveCount int mc; // moveCount
// Init reductions array // Init reductions array
for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++) for (hd = 1; hd < 64; ++hd) for (mc = 1; mc < 64; ++mc)
{ {
double pvRed = log(double(hd)) * log(double(mc)) / 3.0; double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
@@ -144,17 +138,16 @@ void Search::init() {
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY) if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY; Reductions[0][0][hd][mc] += ONE_PLY;
else if (Reductions[0][0][hd][mc] > 1 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY / 2;
} }
// Init futility margins array
for (d = 1; d < 16; d++) for (mc = 0; mc < 64; mc++)
FutilityMargins[d][mc] = Value(112 * int(log(double(d * d) / 2) / log(2.0) + 1.001) - 8 * mc + 45);
// Init futility move count array // Init futility move count array
for (d = 0; d < 32; d++) for (d = 0; d < 32; ++d)
{ {
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4; FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.0, 1.8));
FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8)); FutilityMoveCounts[1][d] = int(3.0 + 0.3 * pow(d + 0.98, 1.8));
} }
} }
@@ -171,7 +164,7 @@ static size_t perft(Position& pos, Depth depth) {
for (MoveList<LEGAL> it(pos); *it; ++it) for (MoveList<LEGAL> it(pos); *it; ++it)
{ {
pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci)); pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY); cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(*it); pos.undo_move(*it);
} }
@@ -237,23 +230,16 @@ void Search::think() {
} }
// Reset the threads, still sleeping: will be wake up at split time // Reset the threads, still sleeping: will be wake up at split time
for (size_t i = 0; i < Threads.size(); i++) for (size_t i = 0; i < Threads.size(); ++i)
Threads[i]->maxPly = 0; Threads[i]->maxPly = 0;
Threads.sleepWhileIdle = Options["Idle Threads Sleep"]; Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
Threads.timer->run = true;
// Set best timer interval to avoid lagging under time pressure. Timer is
// used to check for remaining available thinking time.
Threads.timer->msec =
Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) :
Limits.nodes ? 2 * TimerResolution
: 100;
Threads.timer->notify_one(); // Wake up the recurring timer Threads.timer->notify_one(); // Wake up the recurring timer
id_loop(RootPos); // Let's start searching ! id_loop(RootPos); // Let's start searching !
Threads.timer->msec = 0; // Stop the timer Threads.timer->run = false; // Stop the timer
Threads.sleepWhileIdle = true; // Send idle threads to sleep Threads.sleepWhileIdle = true; // Send idle threads to sleep
if (Options["Write Search Log"]) if (Options["Write Search Log"])
@@ -304,13 +290,14 @@ namespace {
void id_loop(Position& pos) { void id_loop(Position& pos) {
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
int depth, prevBestMoveChanges; int depth;
Value bestValue, alpha, beta, delta; Value bestValue, alpha, beta, delta;
std::memset(ss-2, 0, 5 * sizeof(Stack)); std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = BestMoveChanges = 0; depth = 0;
BestMoveChanges = 0;
bestValue = delta = alpha = -VALUE_INFINITE; bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE; beta = VALUE_INFINITE;
@@ -332,16 +319,16 @@ namespace {
// Iterative deepening loop until requested to stop or target depth reached // Iterative deepening loop until requested to stop or target depth reached
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
{ {
// Age out PV variability metric
BestMoveChanges *= 0.8;
// Save last iteration's scores before first PV line is searched and all // Save last iteration's scores before first PV line is searched and all
// the move scores but the (new) PV are set to -VALUE_INFINITE. // the move scores but the (new) PV are set to -VALUE_INFINITE.
for (size_t i = 0; i < RootMoves.size(); i++) for (size_t i = 0; i < RootMoves.size(); ++i)
RootMoves[i].prevScore = RootMoves[i].score; RootMoves[i].prevScore = RootMoves[i].score;
prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1
BestMoveChanges = 0;
// MultiPV loop. We perform a full root search for each PV line // MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < PVSize; PVIdx++) for (PVIdx = 0; PVIdx < PVSize && !Signals.stop; ++PVIdx)
{ {
// Reset aspiration window starting size // Reset aspiration window starting size
if (depth >= 5) if (depth >= 5)
@@ -367,14 +354,14 @@ namespace {
// Write PV back to transposition table in case the relevant // Write PV back to transposition table in case the relevant
// entries have been overwritten during the search. // entries have been overwritten during the search.
for (size_t i = 0; i <= PVIdx; i++) for (size_t i = 0; i <= PVIdx; ++i)
RootMoves[i].insert_pv_in_tt(pos); RootMoves[i].insert_pv_in_tt(pos);
// If search has been stopped return immediately. Sorting and // If search has been stopped break immediately. Sorting and
// writing PV back to TT is safe becuase RootMoves is still // writing PV back to TT is safe becuase RootMoves is still
// valid, although refers to previous iteration. // valid, although refers to previous iteration.
if (Signals.stop) if (Signals.stop)
return; break;
// When failing high/low give some update (without cluttering // When failing high/low give some update (without cluttering
// the UI) before to research. // the UI) before to research.
@@ -431,13 +418,13 @@ namespace {
Signals.stop = true; Signals.stop = true;
// Do we have time for the next iteration? Can we stop searching now? // Do we have time for the next iteration? Can we stop searching now?
if (Limits.use_time_management() && !Signals.stopOnPonderhit) if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit)
{ {
bool stop = false; // Local variable, not the volatile Signals.stop bool stop = false; // Local variable, not the volatile Signals.stop
// Take in account some extra time if the best move has changed // Take in account some extra time if the best move has changed
if (depth > 4 && depth < 50 && PVSize == 1) if (depth > 4 && depth < 50 && PVSize == 1)
TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges); TimeMgr.pv_instability(BestMoveChanges);
// Stop search if most of available time is already consumed. We // Stop search if most of available time is already consumed. We
// probably don't have enough time to search the first move at the // probably don't have enough time to search the first move at the
@@ -447,6 +434,7 @@ namespace {
// Stop search early if one move seems to be much better than others // Stop search early if one move seems to be much better than others
if ( depth >= 12 if ( depth >= 12
&& BestMoveChanges <= DBL_EPSILON
&& !stop && !stop
&& PVSize == 1 && PVSize == 1
&& bestValue > VALUE_MATED_IN_MAX_PLY && bestValue > VALUE_MATED_IN_MAX_PLY
@@ -502,9 +490,8 @@ namespace {
SplitPoint* splitPoint; SplitPoint* splitPoint;
Key posKey; Key posKey;
Move ttMove, move, excludedMove, bestMove, threatMove; Move ttMove, move, excludedMove, bestMove, threatMove;
Depth ext, newDepth; Depth ext, newDepth, predictedDepth;
Value bestValue, value, ttValue; Value bestValue, value, ttValue, eval, nullValue, futilityValue;
Value eval, nullValue, futilityValue;
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving; bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
bool captureOrPromotion, dangerous, doFullDepthSearch; bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount, quietCount; int moveCount, quietCount;
@@ -532,7 +519,6 @@ namespace {
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1; ss->ply = (ss-1)->ply + 1;
ss->futilityMoveCount = 0;
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
@@ -584,7 +570,7 @@ namespace {
if ( ttValue >= beta if ( ttValue >= beta
&& ttMove && ttMove
&& !pos.is_capture_or_promotion(ttMove) && !pos.capture_or_promotion(ttMove)
&& ttMove != ss->killers[0]) && ttMove != ss->killers[0])
{ {
ss->killers[1] = ss->killers[0]; ss->killers[1] = ss->killers[0];
@@ -596,32 +582,27 @@ namespace {
// Step 5. Evaluate the position statically and update parent's gain statistics // Step 5. Evaluate the position statically and update parent's gain statistics
if (inCheck) if (inCheck)
{ {
ss->staticEval = ss->evalMargin = eval = VALUE_NONE; ss->staticEval = eval = VALUE_NONE;
goto moves_loop; goto moves_loop;
} }
else if (tte) else if (tte)
{ {
// Never assume anything on values stored in TT // Never assume anything on values stored in TT
if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE)
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) eval = ss->staticEval = evaluate(pos);
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
// Can ttValue be used as a better position evaluation? // Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE) if (ttValue != VALUE_NONE)
if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval) if (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))
|| ((tte->bound() & BOUND_UPPER) && ttValue < eval))
eval = ttValue; eval = ttValue;
} }
else else
{ {
eval = ss->staticEval = evaluate(pos, ss->evalMargin); eval = ss->staticEval = evaluate(pos);
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
ss->staticEval, ss->evalMargin);
} }
// Update gain for the parent non-capture move given the static position
// evaluation before and after the move.
if ( !pos.captured_piece_type() if ( !pos.captured_piece_type()
&& ss->staticEval != VALUE_NONE && ss->staticEval != VALUE_NONE
&& (ss-1)->staticEval != VALUE_NONE && (ss-1)->staticEval != VALUE_NONE
@@ -648,22 +629,20 @@ namespace {
return v; return v;
} }
// Step 7. Static null move pruning (skipped when in check) // Step 7. Futility pruning: child node (skipped when in check)
// We're betting that the opponent doesn't have a move that will reduce
// the score by more than futility_margin(depth) if we do a null move.
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth < 4 * ONE_PLY && depth < 7 * ONE_PLY
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta && eval - futility_margin(depth) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& abs(eval) < VALUE_KNOWN_WIN && abs(eval) < VALUE_KNOWN_WIN
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
return eval - futility_margin(depth, (ss-1)->futilityMoveCount); return eval - futility_margin(depth);
// Step 8. Null move search with verification search (is omitted in PV nodes) // Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth > ONE_PLY && depth >= 2 * ONE_PLY
&& eval >= beta && eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
@@ -739,10 +718,10 @@ namespace {
CheckInfo ci(pos); CheckInfo ci(pos);
while ((move = mp.next_move<false>()) != MOVE_NONE) while ((move = mp.next_move<false>()) != MOVE_NONE)
if (pos.pl_move_is_legal(move, ci.pinned)) if (pos.legal(move, ci.pinned))
{ {
ss->currentMove = move; ss->currentMove = move;
pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); pos.do_move(move, st, ci, pos.gives_check(move, ci));
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move); pos.undo_move(move);
if (value >= rbeta) if (value >= rbeta)
@@ -780,7 +759,7 @@ moves_loop: // When in check and at SpNode search starts from here
singularExtensionNode = !RootNode singularExtensionNode = !RootNode
&& !SpNode && !SpNode
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) && depth >= 8 * ONE_PLY
&& ttMove != MOVE_NONE && ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed && !excludedMove // Recursive singular search is not allowed
&& (tte->bound() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
@@ -804,14 +783,14 @@ moves_loop: // When in check and at SpNode search starts from here
if (SpNode) if (SpNode)
{ {
// Shared counter cannot be decremented later if move turns out to be illegal // Shared counter cannot be decremented later if move turns out to be illegal
if (!pos.pl_move_is_legal(move, ci.pinned)) if (!pos.legal(move, ci.pinned))
continue; continue;
moveCount = ++splitPoint->moveCount; moveCount = ++splitPoint->moveCount;
splitPoint->mutex.unlock(); splitPoint->mutex.unlock();
} }
else else
moveCount++; ++moveCount;
if (RootNode) if (RootNode)
{ {
@@ -824,19 +803,16 @@ moves_loop: // When in check and at SpNode search starts from here
} }
ext = DEPTH_ZERO; ext = DEPTH_ZERO;
captureOrPromotion = pos.is_capture_or_promotion(move); captureOrPromotion = pos.capture_or_promotion(move);
givesCheck = pos.move_gives_check(move, ci); givesCheck = pos.gives_check(move, ci);
dangerous = givesCheck dangerous = givesCheck
|| pos.is_passed_pawn_push(move) || pos.passed_pawn_push(move)
|| type_of(move) == CASTLE; || type_of(move) == CASTLE;
// Step 12. Extend checks and, in PV nodes, also dangerous moves // Step 12. Extend checks
if (PvNode && dangerous) if (givesCheck && pos.see_sign(move) >= 0)
ext = ONE_PLY; ext = ONE_PLY;
else if (givesCheck && pos.see_sign(move) >= 0)
ext = ONE_PLY / 2;
// Singular extension search. If all moves but one fail low on a search of // Singular extension search. If all moves but one fail low on a search of
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
// is singular and should be extended. To verify this we do a reduced search // is singular and should be extended. To verify this we do a reduced search
@@ -845,7 +821,7 @@ moves_loop: // When in check and at SpNode search starts from here
if ( singularExtensionNode if ( singularExtensionNode
&& move == ttMove && move == ttMove
&& !ext && !ext
&& pos.pl_move_is_legal(move, ci.pinned) && pos.legal(move, ci.pinned)
&& abs(ttValue) < VALUE_KNOWN_WIN) && abs(ttValue) < VALUE_KNOWN_WIN)
{ {
assert(ttValue != VALUE_NONE); assert(ttValue != VALUE_NONE);
@@ -864,7 +840,7 @@ moves_loop: // When in check and at SpNode search starts from here
// Update current move (this must be done after singular extension search) // Update current move (this must be done after singular extension search)
newDepth = depth - ONE_PLY + ext; newDepth = depth - ONE_PLY + ext;
// Step 13. Futility pruning (is omitted in PV nodes) // Step 13. Pruning at shallow depth (exclude PV nodes)
if ( !PvNode if ( !PvNode
&& !captureOrPromotion && !captureOrPromotion
&& !inCheck && !inCheck
@@ -883,29 +859,30 @@ moves_loop: // When in check and at SpNode search starts from here
continue; continue;
} }
// Value based pruning predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
// but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
+ Gains[pos.piece_moved(move)][to_sq(move)];
if (futilityValue < beta) // Futility pruning: parent node
if (predictedDepth < 7 * ONE_PLY)
{ {
bestValue = std::max(bestValue, futilityValue); futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ Value(128) + Gains[pos.moved_piece(move)][to_sq(move)];
if (SpNode) if (futilityValue <= alpha)
{ {
splitPoint->mutex.lock(); bestValue = std::max(bestValue, futilityValue);
if (bestValue > splitPoint->bestValue)
splitPoint->bestValue = bestValue; if (SpNode)
{
splitPoint->mutex.lock();
if (bestValue > splitPoint->bestValue)
splitPoint->bestValue = bestValue;
}
continue;
} }
continue;
} }
// Prune moves with negative SEE at low depths // Prune moves with negative SEE at low depths
if ( predictedDepth < 4 * ONE_PLY if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < 0)
&& pos.see_sign(move) < 0)
{ {
if (SpNode) if (SpNode)
splitPoint->mutex.lock(); splitPoint->mutex.lock();
@@ -913,15 +890,10 @@ moves_loop: // When in check and at SpNode search starts from here
continue; continue;
} }
// We have not pruned the move that will be searched, but remember how
// far in the move list we are to be more aggressive in the child node.
ss->futilityMoveCount = moveCount;
} }
else
ss->futilityMoveCount = 0;
// Check for legality only before to do the move // Check for legality only before to do the move
if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned)) if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
{ {
moveCount--; moveCount--;
continue; continue;
@@ -937,10 +909,9 @@ moves_loop: // When in check and at SpNode search starts from here
// Step 15. Reduced depth search (LMR). If the move fails high will be // Step 15. Reduced depth search (LMR). If the move fails high will be
// re-searched at full depth. // re-searched at full depth.
if ( depth > 3 * ONE_PLY if ( depth >= 3 * ONE_PLY
&& !pvMove && !pvMove
&& !captureOrPromotion && !captureOrPromotion
&& !dangerous
&& move != ttMove && move != ttMove
&& move != ss->killers[0] && move != ss->killers[0]
&& move != ss->killers[1]) && move != ss->killers[1])
@@ -950,8 +921,11 @@ moves_loop: // When in check and at SpNode search starts from here
if (!PvNode && cutNode) if (!PvNode && cutNode)
ss->reduction += ONE_PLY; ss->reduction += ONE_PLY;
else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
ss->reduction += ONE_PLY / 2;
if (move == countermoves[0] || move == countermoves[1]) if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY); ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode) if (SpNode)
@@ -1019,7 +993,7 @@ moves_loop: // When in check and at SpNode search starts from here
// iteration. This information is used for time management: When // iteration. This information is used for time management: When
// the best move changes frequently, we allocate some more time. // the best move changes frequently, we allocate some more time.
if (!pvMove) if (!pvMove)
BestMoveChanges++; ++BestMoveChanges;
} }
else else
// All other moves but the PV are set to the lowest value, this // All other moves but the PV are set to the lowest value, this
@@ -1086,11 +1060,11 @@ moves_loop: // When in check and at SpNode search starts from here
TT.store(posKey, value_to_tt(bestValue, ss->ply), TT.store(posKey, value_to_tt(bestValue, ss->ply),
bestValue >= beta ? BOUND_LOWER : bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin); depth, bestMove, ss->staticEval);
// Quiet best move: update killers, history and countermoves // Quiet best move: update killers, history and countermoves
if ( bestValue >= beta if ( bestValue >= beta
&& !pos.is_capture_or_promotion(bestMove) && !pos.capture_or_promotion(bestMove)
&& !inCheck) && !inCheck)
{ {
if (ss->killers[0] != bestMove) if (ss->killers[0] != bestMove)
@@ -1102,11 +1076,11 @@ moves_loop: // When in check and at SpNode search starts from here
// Increase history value of the cut-off move and decrease all the other // Increase history value of the cut-off move and decrease all the other
// played non-capture moves. // played non-capture moves.
Value bonus = Value(int(depth) * int(depth)); Value bonus = Value(int(depth) * int(depth));
History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); History.update(pos.moved_piece(bestMove), to_sq(bestMove), bonus);
for (int i = 0; i < quietCount - 1; i++) for (int i = 0; i < quietCount - 1; ++i)
{ {
Move m = quietsSearched[i]; Move m = quietsSearched[i];
History.update(pos.piece_moved(m), to_sq(m), -bonus); History.update(pos.moved_piece(m), to_sq(m), -bonus);
} }
if (is_ok((ss-1)->currentMove)) if (is_ok((ss-1)->currentMove))
@@ -1179,7 +1153,7 @@ moves_loop: // When in check and at SpNode search starts from here
// Evaluate the position statically // Evaluate the position statically
if (InCheck) if (InCheck)
{ {
ss->staticEval = ss->evalMargin = VALUE_NONE; ss->staticEval = VALUE_NONE;
bestValue = futilityBase = -VALUE_INFINITE; bestValue = futilityBase = -VALUE_INFINITE;
} }
else else
@@ -1187,19 +1161,23 @@ moves_loop: // When in check and at SpNode search starts from here
if (tte) if (tte)
{ {
// Never assume anything on values stored in TT // Never assume anything on values stored in TT
if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE)
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos);
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
// Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE)
if (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))
bestValue = ttValue;
} }
else else
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); ss->staticEval = bestValue = evaluate(pos);
// Stand pat. Return immediately if static value is at least beta // Stand pat. Return immediately if static value is at least beta
if (bestValue >= beta) if (bestValue >= beta)
{ {
if (!tte) if (!tte)
TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin); DEPTH_NONE, MOVE_NONE, ss->staticEval);
return bestValue; return bestValue;
} }
@@ -1207,7 +1185,7 @@ moves_loop: // When in check and at SpNode search starts from here
if (PvNode && bestValue > alpha) if (PvNode && bestValue > alpha)
alpha = bestValue; alpha = bestValue;
futilityBase = ss->staticEval + ss->evalMargin + Value(128); futilityBase = bestValue + Value(128);
} }
// Initialize a MovePicker object for the current position, and prepare // Initialize a MovePicker object for the current position, and prepare
@@ -1222,7 +1200,7 @@ moves_loop: // When in check and at SpNode search starts from here
{ {
assert(is_ok(move)); assert(is_ok(move));
givesCheck = pos.move_gives_check(move, ci); givesCheck = pos.gives_check(move, ci);
// Futility pruning // Futility pruning
if ( !PvNode if ( !PvNode
@@ -1230,7 +1208,8 @@ moves_loop: // When in check and at SpNode search starts from here
&& !givesCheck && !givesCheck
&& move != ttMove && move != ttMove
&& type_of(move) != PROMOTION && type_of(move) != PROMOTION
&& !pos.is_passed_pawn_push(move)) && futilityBase > -VALUE_KNOWN_WIN
&& !pos.passed_pawn_push(move))
{ {
futilityValue = futilityBase futilityValue = futilityBase
+ PieceValue[EG][pos.piece_on(to_sq(move))] + PieceValue[EG][pos.piece_on(to_sq(move))]
@@ -1255,7 +1234,7 @@ moves_loop: // When in check and at SpNode search starts from here
// Detect non-capture evasions that are candidate to be pruned // Detect non-capture evasions that are candidate to be pruned
evasionPrunable = InCheck evasionPrunable = InCheck
&& bestValue > VALUE_MATED_IN_MAX_PLY && bestValue > VALUE_MATED_IN_MAX_PLY
&& !pos.is_capture(move) && !pos.capture(move)
&& !pos.can_castle(pos.side_to_move()); && !pos.can_castle(pos.side_to_move());
// Don't search moves with negative SEE values // Don't search moves with negative SEE values
@@ -1266,18 +1245,8 @@ moves_loop: // When in check and at SpNode search starts from here
&& pos.see_sign(move) < 0) && pos.see_sign(move) < 0)
continue; continue;
// Don't search useless checks
if ( !PvNode
&& !InCheck
&& givesCheck
&& move != ttMove
&& !pos.is_capture_or_promotion(move)
&& ss->staticEval + PawnValueMg / 4 < beta
&& !check_is_dangerous(pos, move, futilityBase, beta))
continue;
// Check for legality only before to do the move // Check for legality only before to do the move
if (!pos.pl_move_is_legal(move, ci.pinned)) if (!pos.legal(move, ci.pinned))
continue; continue;
ss->currentMove = move; ss->currentMove = move;
@@ -1305,7 +1274,7 @@ moves_loop: // When in check and at SpNode search starts from here
else // Fail high else // Fail high
{ {
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
ttDepth, move, ss->staticEval, ss->evalMargin); ttDepth, move, ss->staticEval);
return value; return value;
} }
@@ -1320,7 +1289,7 @@ moves_loop: // When in check and at SpNode search starts from here
TT.store(posKey, value_to_tt(bestValue, ss->ply), TT.store(posKey, value_to_tt(bestValue, ss->ply),
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval, ss->evalMargin); ttDepth, bestMove, ss->staticEval);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1353,42 +1322,6 @@ moves_loop: // When in check and at SpNode search starts from here
} }
// check_is_dangerous() tests if a checking move can be pruned in qsearch()
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta)
{
Piece pc = pos.piece_moved(move);
Square from = from_sq(move);
Square to = to_sq(move);
Color them = ~pos.side_to_move();
Square ksq = pos.king_square(them);
Bitboard enemies = pos.pieces(them);
Bitboard kingAtt = pos.attacks_from<KING>(ksq);
Bitboard occ = pos.pieces() ^ from ^ ksq;
Bitboard oldAtt = pos.attacks_from(pc, from, occ);
Bitboard newAtt = pos.attacks_from(pc, to, occ);
// Checks which give opponent's king at most one escape square are dangerous
if (!more_than_one(kingAtt & ~(enemies | newAtt | to)))
return true;
// Queen contact check is very dangerous
if (type_of(pc) == QUEEN && (kingAtt & to))
return true;
// Creating new double threats with checks is dangerous
Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt;
while (b)
{
// Note that here we generate illegal "double move"!
if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta)
return true;
}
return false;
}
// allows() tests whether the 'first' move at previous ply somehow makes the // allows() tests whether the 'first' move at previous ply somehow makes the
// 'second' move possible, for instance if the moving piece is the same in // 'second' move possible, for instance if the moving piece is the same in
// both moves. Normally the second move is the threat (the best move returned // both moves. Normally the second move is the threat (the best move returned
@@ -1399,7 +1332,7 @@ moves_loop: // When in check and at SpNode search starts from here
assert(is_ok(first)); assert(is_ok(first));
assert(is_ok(second)); assert(is_ok(second));
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
Square m1from = from_sq(first); Square m1from = from_sq(first);
Square m2from = from_sq(second); Square m2from = from_sq(second);
@@ -1407,7 +1340,10 @@ moves_loop: // When in check and at SpNode search starts from here
Square m2to = to_sq(second); Square m2to = to_sq(second);
// The piece is the same or second's destination was vacated by the first move // The piece is the same or second's destination was vacated by the first move
if (m1to == m2from || m2to == m1from) // We exclude the trivial case where a sliding piece does in two moves what
// it could do in one move: eg. Ra1a2, Ra2a3.
if ( m2to == m1from
|| (m1to == m2from && !aligned(m1from, m2from, m2to)))
return true; return true;
// Second one moves through the square vacated by first one // Second one moves through the square vacated by first one
@@ -1450,7 +1386,7 @@ moves_loop: // When in check and at SpNode search starts from here
// If the threatened piece has value less than or equal to the value of the // If the threatened piece has value less than or equal to the value of the
// threat piece, don't prune moves which defend it. // threat piece, don't prune moves which defend it.
if ( pos.is_capture(second) if ( pos.capture(second)
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)] && ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|| type_of(pos.piece_on(m2from)) == KING)) || type_of(pos.piece_on(m2from)) == KING))
{ {
@@ -1487,7 +1423,7 @@ moves_loop: // When in check and at SpNode search starts from here
static RKISS rk; static RKISS rk;
// PRNG sequence should be not deterministic // PRNG sequence should be not deterministic
for (int i = Time::now() % 50; i > 0; i--) for (int i = Time::now() % 50; i > 0; --i)
rk.rand<unsigned>(); rk.rand<unsigned>();
// RootMoves are already sorted by score in descending order // RootMoves are already sorted by score in descending order
@@ -1499,7 +1435,7 @@ moves_loop: // When in check and at SpNode search starts from here
// Choose best move. For each move score we add two terms both dependent on // Choose best move. For each move score we add two terms both dependent on
// weakness, one deterministic and bigger for weaker moves, and one random, // weakness, one deterministic and bigger for weaker moves, and one random,
// then we choose the move with the resulting highest score. // then we choose the move with the resulting highest score.
for (size_t i = 0; i < PVSize; i++) for (size_t i = 0; i < PVSize; ++i)
{ {
int s = RootMoves[i].score; int s = RootMoves[i].score;
@@ -1532,11 +1468,11 @@ moves_loop: // When in check and at SpNode search starts from here
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
int selDepth = 0; int selDepth = 0;
for (size_t i = 0; i < Threads.size(); i++) for (size_t i = 0; i < Threads.size(); ++i)
if (Threads[i]->maxPly > selDepth) if (Threads[i]->maxPly > selDepth)
selDepth = Threads[i]->maxPly; selDepth = Threads[i]->maxPly;
for (size_t i = 0; i < uciPVSize; i++) for (size_t i = 0; i < uciPVSize; ++i)
{ {
bool updated = (i <= PVIdx); bool updated = (i <= PVIdx);
@@ -1558,7 +1494,7 @@ moves_loop: // When in check and at SpNode search starts from here
<< " multipv " << i + 1 << " multipv " << i + 1
<< " pv"; << " pv";
for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; ++j)
s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960()); s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960());
} }
@@ -1591,8 +1527,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
tte = TT.probe(pos.key()); tte = TT.probe(pos.key());
} while ( tte } while ( tte
&& pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change && pos.pseudo_legal(m = tte->move()) // Local copy, TT could change
&& pos.pl_move_is_legal(m, pos.pinned_pieces()) && pos.legal(m, pos.pinned_pieces(pos.side_to_move()))
&& ply < MAX_PLY && ply < MAX_PLY
&& (!pos.is_draw() || ply < 2)); && (!pos.is_draw() || ply < 2));
@@ -1616,7 +1552,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
tte = TT.probe(pos.key()); tte = TT.probe(pos.key());
if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries
TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE); TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE);
assert(MoveList<LEGAL>(pos).contains(pv[ply])); assert(MoveList<LEGAL>(pos).contains(pv[ply]));
@@ -1773,8 +1709,8 @@ void check_time() {
// Loop across all split points and sum accumulated SplitPoint nodes plus // Loop across all split points and sum accumulated SplitPoint nodes plus
// all the currently active positions nodes. // all the currently active positions nodes.
for (size_t i = 0; i < Threads.size(); i++) for (size_t i = 0; i < Threads.size(); ++i)
for (int j = 0; j < Threads[i]->splitPointsSize; j++) for (int j = 0; j < Threads[i]->splitPointsSize; ++j)
{ {
SplitPoint& sp = Threads[i]->splitPoints[j]; SplitPoint& sp = Threads[i]->splitPoints[j];
@@ -1800,7 +1736,7 @@ void check_time() {
&& !Signals.failedLowAtRoot && !Signals.failedLowAtRoot
&& elapsed > TimeMgr.available_time(); && elapsed > TimeMgr.available_time();
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution
|| stillAtFirstMove; || stillAtFirstMove;
if ( (Limits.use_time_management() && noMoreTime) if ( (Limits.use_time_management() && noMoreTime)
-2
View File
@@ -45,9 +45,7 @@ struct Stack {
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value staticEval; Value staticEval;
Value evalMargin;
int skipNullMove; int skipNullMove;
int futilityMoveCount;
}; };
+8 -8
View File
@@ -101,11 +101,11 @@ void TimerThread::idle_loop() {
mutex.lock(); mutex.lock();
if (!exit) if (!exit)
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX); sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
mutex.unlock(); mutex.unlock();
if (msec) if (run)
check_time(); check_time();
} }
} }
@@ -157,14 +157,14 @@ bool Thread::cutoff_occurred() const {
} }
// Thread::is_available_to() checks whether the thread is available to help the // Thread::available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must // 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 // be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some split point, it is only available as a slave to the slaves // 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 // which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology). // stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(const Thread* master) const { bool Thread::available_to(const Thread* master) const {
if (searching) if (searching)
return false; return false;
@@ -241,7 +241,7 @@ void ThreadPool::read_uci_options() {
Thread* ThreadPool::available_slave(const Thread* master) const { Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it) for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master)) if ((*it)->available_to(master))
return *it; return *it;
return NULL; return NULL;
@@ -296,7 +296,7 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
Threads.mutex.lock(); Threads.mutex.lock();
sp.mutex.lock(); sp.mutex.lock();
splitPointsSize++; ++splitPointsSize;
activeSplitPoint = &sp; activeSplitPoint = &sp;
activePosition = NULL; activePosition = NULL;
@@ -330,13 +330,13 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
// We have returned from the idle loop, which means that all threads are // We have returned from the idle loop, which means that all threads are
// finished. Note that setting 'searching' and decreasing splitPointsSize is // finished. Note that setting 'searching' and decreasing splitPointsSize is
// done under lock protection to avoid a race with Thread::is_available_to(). // done under lock protection to avoid a race with Thread::available_to().
Threads.mutex.lock(); Threads.mutex.lock();
sp.mutex.lock(); sp.mutex.lock();
} }
searching = true; searching = true;
splitPointsSize--; --splitPointsSize;
activeSplitPoint = sp.parentSplitPoint; activeSplitPoint = sp.parentSplitPoint;
activePosition = &pos; activePosition = &pos;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
+4 -3
View File
@@ -114,7 +114,7 @@ struct Thread : public ThreadBase {
Thread(); Thread();
virtual void idle_loop(); virtual void idle_loop();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(const Thread* master) const; bool available_to(const Thread* master) const;
template <bool Fake> template <bool Fake>
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
@@ -143,9 +143,10 @@ struct MainThread : public Thread {
}; };
struct TimerThread : public ThreadBase { struct TimerThread : public ThreadBase {
TimerThread() : msec(0) {} TimerThread() : run(false) {}
virtual void idle_loop(); virtual void idle_loop();
int msec; bool run;
static const int Resolution = 5; // msec between two check_time() calls
}; };
+12 -13
View File
@@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include "search.h" #include "search.h"
#include "timeman.h" #include "timeman.h"
@@ -29,8 +29,8 @@ namespace {
/// Constants /// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio const double MaxRatio = 7.0; // 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 const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
// MoveImportance[] is based on naive statistical analysis of "how many games are still undecided // MoveImportance[] is based on naive statistical analysis of "how many games are still undecided
@@ -76,10 +76,9 @@ namespace {
} }
void TimeManager::pv_instability(int curChanges, int prevChanges) { void TimeManager::pv_instability(double bestMoveChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2) unstablePVExtraTime = int(bestMoveChanges * optimumSearchTime / 1.4);
+ prevChanges * (optimumSearchTime / 3);
} }
@@ -115,7 +114,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the // We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++) for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
{ {
// Calculate thinking time for hypothetic "moves to go"-value // Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time[us] hypMyTime = limits.time[us]
@@ -145,17 +144,17 @@ namespace {
template<TimeType T> template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly, int slowMover) int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
{ {
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio); const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly) * slowMover / 100; double thisMoveImportance = double(move_importance(currentPly) * slowMover) / 100;
int otherMovesImportance = 0; int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++) for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(currentPly + 2 * i); otherMovesImportance += move_importance(currentPly + 2 * i);
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance); double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance); double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
return int(floor(myTime * std::min(ratio1, ratio2))); return int(floor(myTime * std::min(ratio1, ratio2)));
} }
+1 -1
View File
@@ -26,7 +26,7 @@
class TimeManager { class TimeManager {
public: public:
void init(const Search::LimitsType& limits, int currentPly, Color us); void init(const Search::LimitsType& limits, int currentPly, Color us);
void pv_instability(int curChanges, int prevChanges); void pv_instability(double bestMoveChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; } int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; } int maximum_time() const { return maximumSearchTime; }
+4 -4
View File
@@ -73,7 +73,7 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
const TTEntry* tte = first_entry(key); const TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32; uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++) for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
if (tte->key() == key32) if (tte->key() == key32)
return tte; return tte;
@@ -89,7 +89,7 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// 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. /// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) { void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
int c1, c2, c3; int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
@@ -97,7 +97,7 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
tte = replace = first_entry(key); tte = replace = first_entry(key);
for (unsigned i = 0; i < ClusterSize; i++, tte++) for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
{ {
if (!tte->key() || tte->key() == key32) // Empty or overwrite old if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{ {
@@ -117,5 +117,5 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
replace = tte; replace = tte;
} }
replace->save(key32, v, b, d, m, generation, statV, evalM); replace->save(key32, v, b, d, m, generation, statV);
} }
+4 -6
View File
@@ -36,7 +36,7 @@
struct TTEntry { struct TTEntry {
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
@@ -45,7 +45,6 @@ struct TTEntry {
value16 = (int16_t)v; value16 = (int16_t)v;
depth16 = (int16_t)d; depth16 = (int16_t)d;
evalValue = (int16_t)ev; evalValue = (int16_t)ev;
evalMargin = (int16_t)em;
} }
void set_generation(uint8_t g) { generation8 = g; } void set_generation(uint8_t g) { generation8 = g; }
@@ -56,13 +55,12 @@ struct TTEntry {
Bound bound() const { return (Bound)bound8; } Bound bound() const { return (Bound)bound8; }
int generation() const { return (int)generation8; } int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; } Value eval_value() const { return (Value)evalValue; }
Value eval_margin() const { return (Value)evalMargin; }
private: private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t bound8, generation8; uint8_t bound8, generation8;
int16_t value16, depth16, evalValue, evalMargin; int16_t value16, depth16, evalValue;
}; };
@@ -78,14 +76,14 @@ class TranspositionTable {
public: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
void new_search() { generation++; } void new_search() { ++generation; }
const TTEntry* probe(const Key key) const; const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const; TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const; void refresh(const TTEntry* tte) const;
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
private: private:
uint32_t hashMask; uint32_t hashMask;
+12 -16
View File
@@ -88,7 +88,7 @@ const bool Is64Bit = false;
typedef uint64_t Key; typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int MAX_MOVES = 192; const int MAX_MOVES = 256;
const int MAX_PLY = 100; const int MAX_PLY = 100;
const int MAX_PLY_PLUS_6 = MAX_PLY + 6; const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
@@ -194,9 +194,9 @@ enum Depth {
ONE_PLY = 2, ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY, DEPTH_QS_CHECKS = 0 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -7 * ONE_PLY, DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };
@@ -274,15 +274,15 @@ inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \ inline T operator*(int i, const T d) { return T(i * int(d)); } \
inline T operator*(const T d, int i) { return T(int(d) * i); } \ inline T operator*(const T d, int i) { return T(int(d) * i); } \
inline T operator-(const T d) { return T(-int(d)); } \ inline T operator-(const T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \ inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \ inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; } inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \ #define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \ inline T operator/(const T d, int i) { return T(int(d) / i); } \
inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; } inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_OPERATORS_ON(Value) ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType) ENABLE_OPERATORS_ON(PieceType)
@@ -323,11 +323,11 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) {
} }
inline Color operator~(Color c) { inline Color operator~(Color c) {
return Color(c ^ 1); return Color(c ^ BLACK);
} }
inline Square operator~(Square s) { inline Square operator~(Square s) {
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8 return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
} }
inline Square operator|(File f, Rank r) { inline Square operator|(File f, Rank r) {
@@ -371,10 +371,6 @@ inline Rank rank_of(Square s) {
return Rank(s >> 3); return Rank(s >> 3);
} }
inline Square mirror(Square s) {
return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1
}
inline Square relative_square(Color c, Square s) { inline Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56)); return Square(s ^ (c * 56));
} }
+4 -4
View File
@@ -80,10 +80,10 @@ void init(OptionsMap& o) {
o["MultiPV"] = Option(1, 1, 500); o["MultiPV"] = Option(1, 1, 500);
o["Skill Level"] = Option(20, 0, 20); o["Skill Level"] = Option(20, 0, 20);
o["Emergency Move Horizon"] = Option(40, 0, 50); o["Emergency Move Horizon"] = Option(40, 0, 50);
o["Emergency Base Time"] = Option(200, 0, 30000); o["Emergency Base Time"] = Option(60, 0, 30000);
o["Emergency Move Time"] = Option(70, 0, 5000); o["Emergency Move Time"] = Option(30, 0, 5000);
o["Minimum Thinking Time"] = Option(20, 0, 5000); o["Minimum Thinking Time"] = Option(20, 0, 5000);
o["Slow Mover"] = Option(100, 10, 1000); o["Slow Mover"] = Option(70, 10, 1000);
o["UCI_Chess960"] = Option(false); o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false, on_eval); o["UCI_AnalyseMode"] = Option(false, on_eval);
} }
@@ -94,7 +94,7 @@ void init(OptionsMap& o) {
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); idx++) for (size_t idx = 0; idx < om.size(); ++idx)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it) for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx) if (it->second.idx == idx)
{ {