Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add mate distance pruning and stopping once shortest mate proved [sea…
…rch]

Mate distance pruning is pretty standard. We check for only one
bound out of laziness, which is good enough and doesn't clutter the
code too much.

The stopping rule using the new `mateStop' flag for detecting when
the shortest mate is found needs more study. It works remarkably
well: only the null move seems to break it, but that can be caused
by the null move preventing the shortest mate to be found in the
first place. Without the null move it is still not 100% perfect,
but close: it fails only in a few (<0.5%) of the mate in 5 ("dm 5")
positions, but on none of the "dm -4 .. +4" positions.  It is not
at all obvious how it could work in the first place, but it does.
It appears to be a subtle interaction with mate distance pruning
and fail-soft alpha-beta. To be continued, maybe it can be repaired
for all cases. For now, it is a lot better than the old stopping
rule which was just wrong.
  • Loading branch information
kervinck committed Oct 26, 2015
1 parent b2e140c commit 0abb565
Show file tree
Hide file tree
Showing 9 changed files with 15,701 additions and 14 deletions.
5,100 changes: 5,100 additions & 0 deletions Data/mate5.epd

Large diffs are not rendered by default.

10,000 changes: 10,000 additions & 0 deletions Data/mated.epd

Large diffs are not rendered by default.

500 changes: 500 additions & 0 deletions Data/qmate.epd

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions Data/xmate.epd

This file was deleted.

12 changes: 8 additions & 4 deletions Makefile
Expand Up @@ -64,15 +64,19 @@ $(win32_exe): $(wildcard Source/*) Makefile versions.json

# Run 1 second position tests
easy wac krk5 tt eg ece3: module
python Tools/bmtest.py 1 < Data/$@.epd
python Tools/epdtest.py 1 < Data/$@.epd

# Run 10 second position tests
hard draw nodraw xmate mate bk: module
python Tools/bmtest.py 10 < Data/$@.epd
hard draw nodraw bk: module
python Tools/epdtest.py 10 < Data/$@.epd

# Run 100 second position tests
mate mated qmate mate5: module
python Tools/epdtest.py 100 < Data/$@.epd

# Run 1000 second position tests
nolot: module
python Tools/bmtest.py 1000 < Data/$@.epd
python Tools/epdtest.py 1000 < Data/$@.epd

# Run node count regression test
nodes: module
Expand Down
8 changes: 7 additions & 1 deletion Source/Engine.h
Expand Up @@ -58,7 +58,12 @@ enum {
minMate = -32000, minEval = -29999, minDtz = -31000,
maxMate = 32000, maxEval = 29999, maxDtz = 31000,
};
#define isMateScore(score) (abs(score) >= maxDtz)
#define isDrawScore(score) ((score) == 0)
#define isWinScore(score) ((score) > maxEval)
#define isLossScore(score) ((score) < minEval)
#define isMateWinScore(score) ((score) >= maxDtz)
#define isMateLossScore(score) ((score) <= minDtz)
#define isMateScore(score) (abs(score) >= maxDtz)

struct ttSlot {
uint64_t key;
Expand Down Expand Up @@ -90,6 +95,7 @@ struct Engine {
int rootPlyNumber;
intList searchMoves; // root moves to search, empty means all
volatile bool stopFlag; // aborts search when alarm time exceeded
bool mateStop; // stops the search once the shortest mate is found

// transposition table
struct {
Expand Down
9 changes: 8 additions & 1 deletion Source/search.c
Expand Up @@ -155,12 +155,13 @@ void rootSearch(Engine_t self,
if (setjmp(here) == 0) { // try search
bool stop = false;
for (int iteration=0; iteration<=depth && !stop; iteration++) {
self->mateStop = true;
self->depth = iteration;
self->score = pvSearch(self, iteration, -maxInt, maxInt, 0);
self->seconds = xTime() - startTime;
updateBestAndPonderMove(self);
stop = infoFunction(infoData, null)
|| (self->score + iteration + 2 >= maxMate && iteration > 0) // TODO: remove
|| (isMateScore(self->score) && self->mateStop && self->depth > 0)
|| (targetTime > 0.0 && self->seconds >= 0.5 * targetTime); // TODO: remove
}
} else { // except abort
Expand Down Expand Up @@ -258,6 +259,8 @@ static int pvSearch(Engine_t self, int depth, int alpha, int beta, int pvIndex)
int newDepth = max(0, depth - 1 + extension - reduction);
int newAlpha = max(alpha, bestScore);
int score = -scout(self, newDepth, -(newAlpha+1), 1);
if (isMateWinScore(bestScore) && !isMateScore(score) && !isDrawScore(score))
self->mateStop = false; // shortest mate not yet proven
if (score > bestScore) {
int pvLen = self->pv.len;
pushList(self->pv, moveList[i]);
Expand Down Expand Up @@ -296,6 +299,10 @@ static int scout(Engine_t self, int depth, int alpha, int nodeType)
if (depth == 0) return qSearch(self, alpha);
if (self->stopFlag) longjmp(self->abortTarget, 1); // raise abort

// Mate distance pruning
int mateBound = maxMate - ply(self) - 2;
if (mateBound <= alpha) return mateBound;

int check = inCheck(board(self));
struct Node node;
node.slot = ttRead(self);
Expand Down
68 changes: 68 additions & 0 deletions TODO.txt
@@ -1,4 +1,72 @@

|Todo| Some remaining failures in the mate5 test set:

make mate5:
----------
8/3N4/6p1/1p5p/5b1p/P2k2n1/1B6/3KQ3 w - - acn 138553; acs 0; bm Ba1; ce 32758; dm 5; pv Ba1 h3 Nf6 b4 Qe6 Bd2 Qd6+ Ke3 Qxg3#;
info time 0 depth 0 score cp 799 nodes 1 nps 19600 hashfull 0
info time 0 depth 1 score cp 851 nodes 50 nps 229448 hashfull 0 pv d7e5 f4e5 e1e5
info time 0 depth 2 score cp 851 nodes 117 nps 335201 hashfull 0 pv d7e5 f4e5 e1e5
info time 1 depth 3 score cp 879 nodes 213 nps 412840 hashfull 1 pv d7e5 f4e5 e1e5 d3c4
info time 2 depth 4 score cp 932 nodes 761 nps 442700 hashfull 2 pv d7e5 f4e5 e1e5 d3c4 e5e6 c4d3 e6g6 d3e3
info time 7 depth 5 score cp 1135 nodes 3668 nps 494478 hashfull 9 pv e1e6 g3e4 e6b3 e4c3 b2c3 h4h3 c3e5 d3e4 e5f4 e4f4
info time 30 depth 6 score cp 1159 nodes 20721 nps 690838 hashfull 53 pv e1e6 f4e5 d7e5 d3e4 e6c6 e4f5 b2d4 h4h3
info time 122 depth 7 score cp 1166 nodes 101398 nps 831009 hashfull 253 pv e1e6 f4d6 e6d6 d3e3 b2d4 e3f3 d7e5 f3g2 d6d5 g2h3 d5d8
info time 523 depth 8 score mate 6 nodes 464437 nps 888791 hashfull 842 pv e1c3 d3e4 d7f6 e4f5 c3c8 f5g5 f6h7 g5h6 c8f8 h6h7 f8h8
info time 2033 depth 9 score mate 6 nodes 1818666 nps 894447 hashfull 1000 pv e1c3 d3e4 d7f6 e4f5 c3c8 f5g5 f6h7 g5h6 c8f8 h6h7 f8h8
info time 6892 depth 10 score mate 6 nodes 5978138 nps 867425 hashfull 1000 pv e1c3 d3e4 d7f6 e4f5 c3c8 f5g5 f6h7 g5h6 c8f8 h6h7 f8h8
bestmove e1c3 score 31.989 mate 6
test result FAILED passed 403 total 404
--> +M5 confirmed. NOK

2RR2bK/b1N4p/1pkNp1r1/p3p2r/6P1/n1p5/n5P1/6B1 w - - acn 330710; acs 1; bm g3; ce 32758; dm 5; pv g3 h6 Ncb5+ Kd5 Nxc3+ Nxc3 Nc4+ Ke4 Nd2#;
info time 0 depth 0 score cp -321 nodes 3 nps 47663 hashfull 0 pv g4h5 g6g2
info time 0 depth 1 score cp -224 nodes 120 nps 259174 hashfull 0 pv c7e6 c6d5 g4h5 g6g2
info time 1 depth 2 score cp -224 nodes 217 nps 307384 hashfull 1 pv c7e6 c6d5 g4h5 g6g2
info time 1 depth 3 score cp -224 nodes 304 nps 365349 hashfull 1 pv c7e6 c6d5 g4h5 g6g2
info time 3 depth 4 score cp -200 nodes 1122 nps 382416 hashfull 3 pv c7b5 c6d5 d6c4 d5e4 g4h5 g6g2 c4a3 g2g1 b5a7
info time 11 depth 5 score cp 130 nodes 5037 nps 472200 hashfull 16 pv c7e8 c6d5 e8f6 g6f6 d6e8 d5e4 e8f6 e4f4 f6h5 f4g4 d8g8 g4h5 h8h7
info time 51 depth 6 score cp 344 nodes 31131 nps 609695 hashfull 93 pv c7e8 c6d5 g4h5 c3c2 d6f7 d5e4 e8d6 e4d5 h5g6
info time 289 depth 7 score cp 440 nodes 202970 nps 702761 hashfull 560 pv g4h5 c3c2 c7e6 c6d5 e6c7 d5c6 c7b5 c6d5 h5g6 c2c1q c8c1 a2c1 h8g8
info time 1684 depth 8 score mate 6 nodes 1188902 nps 706139 hashfull 1000 pv c7e6 c6d5 d6f5 d5e6 d8d6 e6f7 c8c7 f7e8 c7e7 e8f8 d6d8
info time 7666 depth 9 score mate 6 nodes 5451505 nps 711118 hashfull 1000 pv c7e6 c6d5 d6f5 d5e6 d8d6 e6f7 c8c7 f7e8 c7e7 e8f8 d6d8
info time 25851 depth 10 score mate 6 nodes 17815758 nps 689162 hashfull 1000 pv c7e6 c6d5 d6f5 d5e6 d8d6 e6f7 c8c7 f7e8 c7e7 e8f8 d6d8
bestmove c7e6 score 31.989 mate 6
test result FAILED passed 632 total 634
--> +M5 confirmed. NOK

6R1/PPPP1P1P/4R3/2BN1N2/7Q/3B1K2/ppPb4/kr6 w - - acn 977087; acs 1; bm h8=B h8=Q; ce 32758; dm 5; pv h8=Q Rf1+ Bxf1 Bc1 Bd3 Bd2 Rg1+ Bc1 Rxc1#;
info time 0 depth 0 score cp 1870 nodes 25 nps 211834 hashfull 0 pv h7h8q
info time 0 depth 1 score cp 1870 nodes 135 nps 356120 hashfull 0 pv h7h8q
info time 2 depth 2 score cp 1955 nodes 674 nps 439925 hashfull 2 pv h7h8q b1f1 d3f1
info time 4 depth 3 score cp 1959 nodes 1861 nps 435216 hashfull 5 pv h7h8q d2g5 f7f8q g5h4 c7c8q
info time 10 depth 4 score cp 1959 nodes 5158 nps 535952 hashfull 16 pv h7h8q d2g5 f7f8q g5h4 c7c8q
info time 22 depth 5 score cp 1959 nodes 17772 nps 801871 hashfull 44 pv h7h8q d2g5 f7f8q g5h4 c7c8q
info time 409 depth 6 score cp 2130 nodes 300232 nps 733225 hashfull 787 pv a7a8q b1f1 d3f1 a1b1 h7h8q a2a1b f7f8q b1c2 d7d8q b2b1r
info time 2644 depth 7 score cp 2130 nodes 1966134 nps 743505 hashfull 1000 pv a7a8q b1f1 d3f1 a1b1 h7h8q a2a1b f7f8q b1c2 d7d8q b2b1r
info time 61975 depth 8 score cp 2293 nodes 44095890 nps 711508 hashfull 1000 pv a7a8q d2a5 h7h8q b1f1 d3f1 a1b1 c7c8q a2a1r f7f8q b1c2 b7b8q b2b1r
info time 100001 nodes 71551481 nps 715509 hashfull 1000
bestmove a7a8q score 22.928 mate None
test result FAILED passed 664 total 667
--> Search explosion & timeout. OK

6RQ/6pP/p5K1/1r2p2N/p3k3/R7/B7/B7 w - - acn 106602; acs 0; bm Qxg7 Rc3; ce 32758; dm 5; pv Rc3 Rb6+ Kg5 Rg6+ Kxg6 a3 Rd8 a5 Bb1#;
info time 0 depth 0 score cp 1056 nodes 12 nps 146313 hashfull 0
info time 0 depth 1 score cp 1138 nodes 99 nps 309416 hashfull 0 pv a3e3 e4e3
info time 0 depth 2 score cp 1139 nodes 180 nps 397355 hashfull 0 pv a3e3 e4e3 a1c3
info time 1 depth 3 score cp 1138 nodes 501 nps 467797 hashfull 2 pv a3e3 e4e3 a1c3 a4a3
info time 4 depth 4 score cp 1231 nodes 1564 nps 438115 hashfull 5 pv h5f6 e4f4 a3f3 f4f3 h8g7 f3g3 h7h8q
info time 12 depth 5 score cp 1231 nodes 6763 nps 559070 hashfull 19 pv h5f6 e4f4 a3f3 f4f3 h8g7 f3g3 h7h8q
info time 56 depth 6 score cp 1400 nodes 41216 nps 731014 hashfull 128 pv h5g3 e4f4 g8f8 f4g4 h8g8 b5b3 g8e6 g4g3 a3b3 g3h2 h7h8q h2g2
info time 343 depth 7 score mate 6 nodes 268270 nps 781081 hashfull 721 pv h5g3 e4f4 g8f8 f4g4 a2e6 g4h4 f8f4 e5f4 h8d8 b5g5 d8g5
info time 2235 depth 8 score mate 6 nodes 1769253 nps 791677 hashfull 1000 pv h5g3 e4f4 g8f8 f4g4 a2e6 g4h4 f8f4 e5f4 h8d8 b5g5 d8g5
info time 10989 depth 9 score mate 6 nodes 8973364 nps 816589 hashfull 1000 pv h5g3 e4f4 g8f8 f4g4 a2e6 g4h4 f8f4 e5f4 h8d8 b5g5 d8g5
bestmove h5g3 score 31.989 mate 6
test result FAILED passed 770 total 774
--> +M5 confirmed. NOK

|Todo| Don't allow 0 as heuristic evalation [evaluate]

|Todo| Introduce move flags for 32 bit

- Simplify UCI move formatting
Expand Down
13 changes: 10 additions & 3 deletions Tools/bmtest.py → Tools/epdtest.py
Expand Up @@ -10,7 +10,7 @@ def parseEpd(rawLine):
pos = ' '.join(line[0:4])

# EPD fields
operations = {'bm': '', 'am': ''}
operations = {'bm': '', 'am': '', 'dm': ''}
fields = [op for op in line[4].split(';') if len(op) > 0]
fields = [op.strip().split(' ', 1) for op in fields]
operations.update(dict(fields))
Expand All @@ -28,11 +28,18 @@ def parseEpd(rawLine):
pos, operations = parseEpd(rawLine)
bm = [chessmoves.move(pos, bm, notation='uci')[0] for bm in operations['bm'].split()] # best move
am = [chessmoves.move(pos, am, notation='uci')[0] for am in operations['am'].split()] # avoid move
dm = [int(dm) for dm in operations['dm'].split()] # mate distance
score, move = engine.search(pos, movetime=movetime, info='uci')
print 'bestmove', move
mate = None
if score >= 31.0: mate = 32.0 - score
if score <= -31.0: mate = -32.0 - score
if mate is not None:
mate = (int(round(mate * 1000.0)) + 1) // 2
print 'bestmove', move, 'score', score, 'mate', mate
print 'test',
if (len(bm) == 0 or move in bm) and\
(len(am) == 0 or move not in am):
(len(am) == 0 or move not in am) and\
(len(dm) == 0 or mate in dm):
print 'result OK',
nrPassed += 1
else:
Expand Down

0 comments on commit 0abb565

Please sign in to comment.