Rev 154 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
96 | pmbaty | 1 | /* |
2 | Copyright (c) 2013 Ronald de Man |
||
3 | This file may be redistributed and/or modified without restrictions. |
||
4 | |||
5 | tbprobe.cpp contains the Stockfish-specific routines of the |
||
6 | tablebase probing code. It should be relatively easy to adapt |
||
7 | this code to other chess engines. |
||
8 | */ |
||
9 | |||
10 | #define NOMINMAX |
||
11 | |||
12 | #include <algorithm> |
||
13 | |||
14 | #include "../position.h" |
||
15 | #include "../movegen.h" |
||
16 | #include "../bitboard.h" |
||
17 | #include "../search.h" |
||
18 | #include "../bitcount.h" |
||
19 | |||
20 | #include "tbprobe.h" |
||
21 | #include "tbcore.h" |
||
22 | |||
23 | #include "tbcore.cpp" |
||
24 | |||
25 | namespace Zobrist { |
||
26 | extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; |
||
27 | } |
||
28 | |||
29 | int Tablebases::MaxCardinality = 0; |
||
30 | |||
31 | // Given a position with 6 or fewer pieces, produce a text string |
||
32 | // of the form KQPvKRP, where "KQP" represents the white pieces if |
||
33 | // mirror == 0 and the black pieces if mirror == 1. |
||
34 | static void prt_str(Position& pos, char *str, int mirror) |
||
35 | { |
||
36 | Color color; |
||
37 | PieceType pt; |
||
38 | int i; |
||
39 | |||
40 | color = !mirror ? WHITE : BLACK; |
||
41 | for (pt = KING; pt >= PAWN; --pt) |
||
42 | for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--) |
||
43 | *str++ = pchr[6 - pt]; |
||
44 | *str++ = 'v'; |
||
45 | color = ~color; |
||
46 | for (pt = KING; pt >= PAWN; --pt) |
||
47 | for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--) |
||
48 | *str++ = pchr[6 - pt]; |
||
49 | *str++ = 0; |
||
50 | } |
||
51 | |||
52 | // Given a position, produce a 64-bit material signature key. |
||
53 | // If the engine supports such a key, it should equal the engine's key. |
||
54 | static uint64 calc_key(Position& pos, int mirror) |
||
55 | { |
||
56 | Color color; |
||
57 | PieceType pt; |
||
58 | int i; |
||
59 | uint64 key = 0; |
||
60 | |||
61 | color = !mirror ? WHITE : BLACK; |
||
62 | for (pt = PAWN; pt <= KING; ++pt) |
||
63 | for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--) |
||
64 | key ^= Zobrist::psq[WHITE][pt][i - 1]; |
||
65 | color = ~color; |
||
66 | for (pt = PAWN; pt <= KING; ++pt) |
||
67 | for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--) |
||
68 | key ^= Zobrist::psq[BLACK][pt][i - 1]; |
||
69 | |||
70 | return key; |
||
71 | } |
||
72 | |||
73 | // Produce a 64-bit material key corresponding to the material combination |
||
74 | // defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white |
||
75 | // pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black |
||
76 | // pawns, ..., kings. |
||
77 | static uint64 calc_key_from_pcs(int *pcs, int mirror) |
||
78 | { |
||
79 | int color; |
||
80 | PieceType pt; |
||
81 | int i; |
||
82 | uint64 key = 0; |
||
83 | |||
84 | color = !mirror ? 0 : 8; |
||
85 | for (pt = PAWN; pt <= KING; ++pt) |
||
86 | for (i = 0; i < pcs[color + pt]; i++) |
||
87 | key ^= Zobrist::psq[WHITE][pt][i]; |
||
88 | color ^= 8; |
||
89 | for (pt = PAWN; pt <= KING; ++pt) |
||
90 | for (i = 0; i < pcs[color + pt]; i++) |
||
91 | key ^= Zobrist::psq[BLACK][pt][i]; |
||
92 | |||
93 | return key; |
||
94 | } |
||
95 | |||
96 | bool is_little_endian() { |
||
97 | union { |
||
98 | int i; |
||
99 | char c[sizeof(int)]; |
||
100 | } x; |
||
101 | x.i = 1; |
||
102 | return x.c[0] == 1; |
||
103 | } |
||
104 | |||
105 | static ubyte decompress_pairs(struct PairsData *d, uint64 idx) |
||
106 | { |
||
107 | static const bool isLittleEndian = is_little_endian(); |
||
108 | return isLittleEndian ? decompress_pairs<true >(d, idx) |
||
109 | : decompress_pairs<false>(d, idx); |
||
110 | } |
||
111 | |||
112 | // probe_wdl_table and probe_dtz_table require similar adaptations. |
||
113 | static int probe_wdl_table(Position& pos, int *success) |
||
114 | { |
||
115 | struct TBEntry *ptr; |
||
116 | struct TBHashEntry *ptr2; |
||
117 | uint64 idx; |
||
118 | uint64 key; |
||
119 | int i; |
||
120 | ubyte res; |
||
121 | int p[TBPIECES]; |
||
122 | |||
123 | // Obtain the position's material signature key. |
||
124 | key = pos.material_key(); |
||
125 | |||
126 | // Test for KvK. |
||
127 | if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0])) |
||
128 | return 0; |
||
129 | |||
130 | ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; |
||
131 | for (i = 0; i < HSHMAX; i++) |
||
132 | if (ptr2[i].key == key) break; |
||
133 | if (i == HSHMAX) { |
||
134 | *success = 0; |
||
135 | return 0; |
||
136 | } |
||
137 | |||
138 | ptr = ptr2[i].ptr; |
||
139 | if (!ptr->ready) { |
||
140 | LOCK(TB_mutex); |
||
141 | if (!ptr->ready) { |
||
142 | char str[16]; |
||
143 | prt_str(pos, str, ptr->key != key); |
||
144 | if (!init_table_wdl(ptr, str)) { |
||
145 | ptr2[i].key = 0ULL; |
||
146 | *success = 0; |
||
147 | UNLOCK(TB_mutex); |
||
148 | return 0; |
||
149 | } |
||
150 | // Memory barrier to ensure ptr->ready = 1 is not reordered. |
||
151 | #ifdef _MSC_VER |
||
152 | _ReadWriteBarrier(); |
||
153 | #else |
||
154 | __asm__ __volatile__ ("" ::: "memory"); |
||
155 | #endif |
||
156 | ptr->ready = 1; |
||
157 | } |
||
158 | UNLOCK(TB_mutex); |
||
159 | } |
||
160 | |||
161 | int bside, mirror, cmirror; |
||
162 | if (!ptr->symmetric) { |
||
163 | if (key != ptr->key) { |
||
164 | cmirror = 8; |
||
165 | mirror = 0x38; |
||
166 | bside = (pos.side_to_move() == WHITE); |
||
167 | } else { |
||
168 | cmirror = mirror = 0; |
||
169 | bside = !(pos.side_to_move() == WHITE); |
||
170 | } |
||
171 | } else { |
||
172 | cmirror = pos.side_to_move() == WHITE ? 0 : 8; |
||
173 | mirror = pos.side_to_move() == WHITE ? 0 : 0x38; |
||
174 | bside = 0; |
||
175 | } |
||
176 | |||
177 | // p[i] is to contain the square 0-63 (A1-H8) for a piece of type |
||
178 | // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. |
||
179 | // Pieces of the same type are guaranteed to be consecutive. |
||
180 | if (!ptr->has_pawns) { |
||
181 | struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; |
||
182 | ubyte *pc = entry->pieces[bside]; |
||
183 | for (i = 0; i < entry->num;) { |
||
184 | Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), |
||
185 | (PieceType)(pc[i] & 0x07)); |
||
186 | do { |
||
187 | p[i++] = pop_lsb(&bb); |
||
188 | } while (bb); |
||
189 | } |
||
190 | idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); |
||
191 | res = decompress_pairs(entry->precomp[bside], idx); |
||
192 | } else { |
||
193 | struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; |
||
194 | int k = entry->file[0].pieces[0][0] ^ cmirror; |
||
195 | Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); |
||
196 | i = 0; |
||
197 | do { |
||
198 | p[i++] = pop_lsb(&bb) ^ mirror; |
||
199 | } while (bb); |
||
200 | int f = pawn_file(entry, p); |
||
201 | ubyte *pc = entry->file[f].pieces[bside]; |
||
202 | for (; i < entry->num;) { |
||
203 | bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), |
||
204 | (PieceType)(pc[i] & 0x07)); |
||
205 | do { |
||
206 | p[i++] = pop_lsb(&bb) ^ mirror; |
||
207 | } while (bb); |
||
208 | } |
||
209 | idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]); |
||
210 | res = decompress_pairs(entry->file[f].precomp[bside], idx); |
||
211 | } |
||
212 | |||
213 | return ((int)res) - 2; |
||
214 | } |
||
215 | |||
216 | static int probe_dtz_table(Position& pos, int wdl, int *success) |
||
217 | { |
||
218 | struct TBEntry *ptr; |
||
219 | uint64 idx; |
||
220 | int i, res; |
||
221 | int p[TBPIECES]; |
||
222 | |||
223 | // Obtain the position's material signature key. |
||
224 | uint64 key = pos.material_key(); |
||
225 | |||
226 | if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { |
||
227 | for (i = 1; i < DTZ_ENTRIES; i++) |
||
228 | if (DTZ_table[i].key1 == key) break; |
||
229 | if (i < DTZ_ENTRIES) { |
||
230 | struct DTZTableEntry table_entry = DTZ_table[i]; |
||
231 | for (; i > 0; i--) |
||
232 | DTZ_table[i] = DTZ_table[i - 1]; |
||
233 | DTZ_table[0] = table_entry; |
||
234 | } else { |
||
235 | struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; |
||
236 | for (i = 0; i < HSHMAX; i++) |
||
237 | if (ptr2[i].key == key) break; |
||
238 | if (i == HSHMAX) { |
||
239 | *success = 0; |
||
240 | return 0; |
||
241 | } |
||
242 | ptr = ptr2[i].ptr; |
||
243 | char str[16]; |
||
244 | int mirror = (ptr->key != key); |
||
245 | prt_str(pos, str, mirror); |
||
246 | if (DTZ_table[DTZ_ENTRIES - 1].entry) |
||
247 | free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); |
||
248 | for (i = DTZ_ENTRIES - 1; i > 0; i--) |
||
249 | DTZ_table[i] = DTZ_table[i - 1]; |
||
250 | load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror)); |
||
251 | } |
||
252 | } |
||
253 | |||
254 | ptr = DTZ_table[0].entry; |
||
255 | if (!ptr) { |
||
256 | *success = 0; |
||
257 | return 0; |
||
258 | } |
||
259 | |||
260 | int bside, mirror, cmirror; |
||
261 | if (!ptr->symmetric) { |
||
262 | if (key != ptr->key) { |
||
263 | cmirror = 8; |
||
264 | mirror = 0x38; |
||
265 | bside = (pos.side_to_move() == WHITE); |
||
266 | } else { |
||
267 | cmirror = mirror = 0; |
||
268 | bside = !(pos.side_to_move() == WHITE); |
||
269 | } |
||
270 | } else { |
||
271 | cmirror = pos.side_to_move() == WHITE ? 0 : 8; |
||
272 | mirror = pos.side_to_move() == WHITE ? 0 : 0x38; |
||
273 | bside = 0; |
||
274 | } |
||
275 | |||
276 | if (!ptr->has_pawns) { |
||
277 | struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; |
||
278 | if ((entry->flags & 1) != bside && !entry->symmetric) { |
||
279 | *success = -1; |
||
280 | return 0; |
||
281 | } |
||
282 | ubyte *pc = entry->pieces; |
||
283 | for (i = 0; i < entry->num;) { |
||
284 | Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), |
||
285 | (PieceType)(pc[i] & 0x07)); |
||
286 | do { |
||
287 | p[i++] = pop_lsb(&bb); |
||
288 | } while (bb); |
||
289 | } |
||
290 | idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor); |
||
291 | res = decompress_pairs(entry->precomp, idx); |
||
292 | |||
293 | if (entry->flags & 2) |
||
294 | res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res]; |
||
295 | |||
296 | if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) |
||
297 | res *= 2; |
||
298 | } else { |
||
299 | struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; |
||
300 | int k = entry->file[0].pieces[0] ^ cmirror; |
||
301 | Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); |
||
302 | i = 0; |
||
303 | do { |
||
304 | p[i++] = pop_lsb(&bb) ^ mirror; |
||
305 | } while (bb); |
||
306 | int f = pawn_file((struct TBEntry_pawn *)entry, p); |
||
307 | if ((entry->flags[f] & 1) != bside) { |
||
308 | *success = -1; |
||
309 | return 0; |
||
310 | } |
||
311 | ubyte *pc = entry->file[f].pieces; |
||
312 | for (; i < entry->num;) { |
||
313 | bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), |
||
314 | (PieceType)(pc[i] & 0x07)); |
||
315 | do { |
||
316 | p[i++] = pop_lsb(&bb) ^ mirror; |
||
317 | } while (bb); |
||
318 | } |
||
319 | idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor); |
||
320 | res = decompress_pairs(entry->file[f].precomp, idx); |
||
321 | |||
322 | if (entry->flags[f] & 2) |
||
323 | res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res]; |
||
324 | |||
325 | if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1)) |
||
326 | res *= 2; |
||
327 | } |
||
328 | |||
329 | return res; |
||
330 | } |
||
331 | |||
332 | // Add underpromotion captures to list of captures. |
||
333 | static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end) |
||
334 | { |
||
335 | ExtMove *moves, *extra = end; |
||
336 | |||
337 | for (moves = stack; moves < end; moves++) { |
||
338 | Move move = moves->move; |
||
339 | if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) { |
||
340 | (*extra++).move = (Move)(move - (1 << 12)); |
||
341 | (*extra++).move = (Move)(move - (2 << 12)); |
||
342 | (*extra++).move = (Move)(move - (3 << 12)); |
||
343 | } |
||
344 | } |
||
345 | |||
346 | return extra; |
||
347 | } |
||
348 | |||
349 | static int probe_ab(Position& pos, int alpha, int beta, int *success) |
||
350 | { |
||
351 | int v; |
||
352 | ExtMove stack[64]; |
||
353 | ExtMove *moves, *end; |
||
354 | StateInfo st; |
||
355 | |||
356 | // Generate (at least) all legal non-ep captures including (under)promotions. |
||
357 | // It is OK to generate more, as long as they are filtered out below. |
||
358 | if (!pos.checkers()) { |
||
359 | end = generate<CAPTURES>(pos, stack); |
||
360 | // Since underpromotion captures are not included, we need to add them. |
||
361 | end = add_underprom_caps(pos, stack, end); |
||
362 | } else |
||
363 | end = generate<EVASIONS>(pos, stack); |
||
364 | |||
365 | CheckInfo ci(pos); |
||
366 | |||
367 | for (moves = stack; moves < end; moves++) { |
||
368 | Move capture = moves->move; |
||
369 | if (!pos.capture(capture) || type_of(capture) == ENPASSANT |
||
370 | || !pos.legal(capture, ci.pinned)) |
||
371 | continue; |
||
372 | pos.do_move(capture, st, pos.gives_check(capture, ci)); |
||
373 | v = -probe_ab(pos, -beta, -alpha, success); |
||
374 | pos.undo_move(capture); |
||
375 | if (*success == 0) return 0; |
||
376 | if (v > alpha) { |
||
377 | if (v >= beta) { |
||
378 | *success = 2; |
||
379 | return v; |
||
380 | } |
||
381 | alpha = v; |
||
382 | } |
||
383 | } |
||
384 | |||
385 | v = probe_wdl_table(pos, success); |
||
386 | if (*success == 0) return 0; |
||
387 | if (alpha >= v) { |
||
388 | *success = 1 + (alpha > 0); |
||
389 | return alpha; |
||
390 | } else { |
||
391 | *success = 1; |
||
392 | return v; |
||
393 | } |
||
394 | } |
||
395 | |||
396 | // Probe the WDL table for a particular position. |
||
397 | // If *success != 0, the probe was successful. |
||
398 | // The return value is from the point of view of the side to move: |
||
399 | // -2 : loss |
||
400 | // -1 : loss, but draw under 50-move rule |
||
401 | // 0 : draw |
||
402 | // 1 : win, but draw under 50-move rule |
||
403 | // 2 : win |
||
404 | int Tablebases::probe_wdl(Position& pos, int *success) |
||
405 | { |
||
406 | int v; |
||
407 | |||
408 | *success = 1; |
||
409 | v = probe_ab(pos, -2, 2, success); |
||
410 | |||
411 | // If en passant is not possible, we are done. |
||
412 | if (pos.ep_square() == SQ_NONE) |
||
413 | return v; |
||
414 | if (!(*success)) return 0; |
||
415 | |||
416 | // Now handle en passant. |
||
417 | int v1 = -3; |
||
418 | // Generate (at least) all legal en passant captures. |
||
419 | ExtMove stack[192]; |
||
420 | ExtMove *moves, *end; |
||
421 | StateInfo st; |
||
422 | |||
423 | if (!pos.checkers()) |
||
424 | end = generate<CAPTURES>(pos, stack); |
||
425 | else |
||
426 | end = generate<EVASIONS>(pos, stack); |
||
427 | |||
428 | CheckInfo ci(pos); |
||
429 | |||
430 | for (moves = stack; moves < end; moves++) { |
||
431 | Move capture = moves->move; |
||
432 | if (type_of(capture) != ENPASSANT |
||
433 | || !pos.legal(capture, ci.pinned)) |
||
434 | continue; |
||
435 | pos.do_move(capture, st, pos.gives_check(capture, ci)); |
||
436 | int v0 = -probe_ab(pos, -2, 2, success); |
||
437 | pos.undo_move(capture); |
||
438 | if (*success == 0) return 0; |
||
439 | if (v0 > v1) v1 = v0; |
||
440 | } |
||
441 | if (v1 > -3) { |
||
442 | if (v1 >= v) v = v1; |
||
443 | else if (v == 0) { |
||
444 | // Check whether there is at least one legal non-ep move. |
||
445 | for (moves = stack; moves < end; moves++) { |
||
446 | Move capture = moves->move; |
||
447 | if (type_of(capture) == ENPASSANT) continue; |
||
448 | if (pos.legal(capture, ci.pinned)) break; |
||
449 | } |
||
450 | if (moves == end && !pos.checkers()) { |
||
451 | end = generate<QUIETS>(pos, end); |
||
452 | for (; moves < end; moves++) { |
||
453 | Move move = moves->move; |
||
454 | if (pos.legal(move, ci.pinned)) |
||
455 | break; |
||
456 | } |
||
457 | } |
||
458 | // If not, then we are forced to play the losing ep capture. |
||
459 | if (moves == end) |
||
460 | v = v1; |
||
461 | } |
||
462 | } |
||
463 | |||
464 | return v; |
||
465 | } |
||
466 | |||
467 | // This routine treats a position with en passant captures as one without. |
||
468 | static int probe_dtz_no_ep(Position& pos, int *success) |
||
469 | { |
||
470 | int wdl, dtz; |
||
471 | |||
472 | wdl = probe_ab(pos, -2, 2, success); |
||
473 | if (*success == 0) return 0; |
||
474 | |||
475 | if (wdl == 0) return 0; |
||
476 | |||
477 | if (*success == 2) |
||
478 | return wdl == 2 ? 1 : 101; |
||
479 | |||
480 | ExtMove stack[192]; |
||
481 | ExtMove *moves, *end = NULL; |
||
482 | StateInfo st; |
||
483 | CheckInfo ci(pos); |
||
484 | |||
485 | if (wdl > 0) { |
||
486 | // Generate at least all legal non-capturing pawn moves |
||
487 | // including non-capturing promotions. |
||
488 | if (!pos.checkers()) |
||
489 | end = generate<NON_EVASIONS>(pos, stack); |
||
490 | else |
||
491 | end = generate<EVASIONS>(pos, stack); |
||
492 | |||
493 | for (moves = stack; moves < end; moves++) { |
||
494 | Move move = moves->move; |
||
495 | if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) |
||
496 | || !pos.legal(move, ci.pinned)) |
||
497 | continue; |
||
498 | pos.do_move(move, st, pos.gives_check(move, ci)); |
||
499 | int v = -probe_ab(pos, -2, -wdl + 1, success); |
||
500 | pos.undo_move(move); |
||
501 | if (*success == 0) return 0; |
||
502 | if (v == wdl) |
||
503 | return v == 2 ? 1 : 101; |
||
504 | } |
||
505 | } |
||
506 | |||
507 | dtz = 1 + probe_dtz_table(pos, wdl, success); |
||
508 | if (*success >= 0) { |
||
509 | if (wdl & 1) dtz += 100; |
||
510 | return wdl >= 0 ? dtz : -dtz; |
||
511 | } |
||
512 | |||
513 | if (wdl > 0) { |
||
514 | int best = 0xffff; |
||
515 | for (moves = stack; moves < end; moves++) { |
||
516 | Move move = moves->move; |
||
517 | if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN |
||
518 | || !pos.legal(move, ci.pinned)) |
||
519 | continue; |
||
520 | pos.do_move(move, st, pos.gives_check(move, ci)); |
||
521 | int v = -Tablebases::probe_dtz(pos, success); |
||
522 | pos.undo_move(move); |
||
523 | if (*success == 0) return 0; |
||
524 | if (v > 0 && v + 1 < best) |
||
525 | best = v + 1; |
||
526 | } |
||
527 | return best; |
||
528 | } else { |
||
529 | int best = -1; |
||
530 | if (!pos.checkers()) |
||
531 | end = generate<NON_EVASIONS>(pos, stack); |
||
532 | else |
||
533 | end = generate<EVASIONS>(pos, stack); |
||
534 | for (moves = stack; moves < end; moves++) { |
||
535 | int v; |
||
536 | Move move = moves->move; |
||
537 | if (!pos.legal(move, ci.pinned)) |
||
538 | continue; |
||
539 | pos.do_move(move, st, pos.gives_check(move, ci)); |
||
540 | if (st.rule50 == 0) { |
||
541 | if (wdl == -2) v = -1; |
||
542 | else { |
||
543 | v = probe_ab(pos, 1, 2, success); |
||
544 | v = (v == 2) ? 0 : -101; |
||
545 | } |
||
546 | } else { |
||
547 | v = -Tablebases::probe_dtz(pos, success) - 1; |
||
548 | } |
||
549 | pos.undo_move(move); |
||
550 | if (*success == 0) return 0; |
||
551 | if (v < best) |
||
552 | best = v; |
||
553 | } |
||
554 | return best; |
||
555 | } |
||
556 | } |
||
557 | |||
558 | static int wdl_to_dtz[] = { |
||
559 | -1, -101, 0, 101, 1 |
||
560 | }; |
||
561 | |||
562 | // Probe the DTZ table for a particular position. |
||
563 | // If *success != 0, the probe was successful. |
||
564 | // The return value is from the point of view of the side to move: |
||
565 | // n < -100 : loss, but draw under 50-move rule |
||
566 | // -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) |
||
567 | // 0 : draw |
||
568 | // 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) |
||
569 | // 100 < n : win, but draw under 50-move rule |
||
570 | // |
||
571 | // The return value n can be off by 1: a return value -n can mean a loss |
||
572 | // in n+1 ply and a return value +n can mean a win in n+1 ply. This |
||
573 | // cannot happen for tables with positions exactly on the "edge" of |
||
574 | // the 50-move rule. |
||
575 | // |
||
576 | // This implies that if dtz > 0 is returned, the position is certainly |
||
577 | // a win if dtz + 50-move-counter <= 99. Care must be taken that the engine |
||
578 | // picks moves that preserve dtz + 50-move-counter <= 99. |
||
579 | // |
||
580 | // If n = 100 immediately after a capture or pawn move, then the position |
||
581 | // is also certainly a win, and during the whole phase until the next |
||
582 | // capture or pawn move, the inequality to be preserved is |
||
583 | // dtz + 50-movecounter <= 100. |
||
584 | // |
||
585 | // In short, if a move is available resulting in dtz + 50-move-counter <= 99, |
||
586 | // then do not accept moves leading to dtz + 50-move-counter == 100. |
||
587 | // |
||
588 | int Tablebases::probe_dtz(Position& pos, int *success) |
||
589 | { |
||
590 | *success = 1; |
||
591 | int v = probe_dtz_no_ep(pos, success); |
||
592 | |||
593 | if (pos.ep_square() == SQ_NONE) |
||
594 | return v; |
||
595 | if (*success == 0) return 0; |
||
596 | |||
597 | // Now handle en passant. |
||
598 | int v1 = -3; |
||
599 | |||
600 | ExtMove stack[192]; |
||
601 | ExtMove *moves, *end; |
||
602 | StateInfo st; |
||
603 | |||
604 | if (!pos.checkers()) |
||
605 | end = generate<CAPTURES>(pos, stack); |
||
606 | else |
||
607 | end = generate<EVASIONS>(pos, stack); |
||
608 | CheckInfo ci(pos); |
||
609 | |||
610 | for (moves = stack; moves < end; moves++) { |
||
611 | Move capture = moves->move; |
||
612 | if (type_of(capture) != ENPASSANT |
||
613 | || !pos.legal(capture, ci.pinned)) |
||
614 | continue; |
||
615 | pos.do_move(capture, st, pos.gives_check(capture, ci)); |
||
616 | int v0 = -probe_ab(pos, -2, 2, success); |
||
617 | pos.undo_move(capture); |
||
618 | if (*success == 0) return 0; |
||
619 | if (v0 > v1) v1 = v0; |
||
620 | } |
||
621 | if (v1 > -3) { |
||
622 | v1 = wdl_to_dtz[v1 + 2]; |
||
623 | if (v < -100) { |
||
624 | if (v1 >= 0) |
||
625 | v = v1; |
||
626 | } else if (v < 0) { |
||
627 | if (v1 >= 0 || v1 < -100) |
||
628 | v = v1; |
||
629 | } else if (v > 100) { |
||
630 | if (v1 > 0) |
||
631 | v = v1; |
||
632 | } else if (v > 0) { |
||
633 | if (v1 == 1) |
||
634 | v = v1; |
||
635 | } else if (v1 >= 0) { |
||
636 | v = v1; |
||
637 | } else { |
||
638 | for (moves = stack; moves < end; moves++) { |
||
639 | Move move = moves->move; |
||
640 | if (type_of(move) == ENPASSANT) continue; |
||
641 | if (pos.legal(move, ci.pinned)) break; |
||
642 | } |
||
643 | if (moves == end && !pos.checkers()) { |
||
644 | end = generate<QUIETS>(pos, end); |
||
645 | for (; moves < end; moves++) { |
||
646 | Move move = moves->move; |
||
647 | if (pos.legal(move, ci.pinned)) |
||
648 | break; |
||
649 | } |
||
650 | } |
||
651 | if (moves == end) |
||
652 | v = v1; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | return v; |
||
657 | } |
||
658 | |||
659 | // Check whether there has been at least one repetition of positions |
||
660 | // since the last capture or pawn move. |
||
661 | static int has_repeated(StateInfo *st) |
||
662 | { |
||
663 | while (1) { |
||
664 | int i = 4, e = std::min(st->rule50, st->pliesFromNull); |
||
665 | if (e < i) |
||
666 | return 0; |
||
667 | StateInfo *stp = st->previous->previous; |
||
668 | do { |
||
669 | stp = stp->previous->previous; |
||
670 | if (stp->key == st->key) |
||
671 | return 1; |
||
672 | i += 2; |
||
673 | } while (i <= e); |
||
674 | st = st->previous; |
||
675 | } |
||
676 | } |
||
677 | |||
678 | static Value wdl_to_Value[5] = { |
||
679 | -VALUE_MATE + MAX_PLY + 1, |
||
680 | VALUE_DRAW - 2, |
||
681 | VALUE_DRAW, |
||
682 | VALUE_DRAW + 2, |
||
683 | VALUE_MATE - MAX_PLY - 1 |
||
684 | }; |
||
685 | |||
686 | // Use the DTZ tables to filter out moves that don't preserve the win or draw. |
||
687 | // If the position is lost, but DTZ is fairly high, only keep moves that |
||
688 | // maximise DTZ. |
||
689 | // |
||
690 | // A return value false indicates that not all probes were successful and that |
||
691 | // no moves were filtered out. |
||
692 | bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score) |
||
693 | { |
||
694 | int success; |
||
695 | |||
696 | int dtz = probe_dtz(pos, &success); |
||
697 | if (!success) return false; |
||
698 | |||
699 | StateInfo st; |
||
700 | CheckInfo ci(pos); |
||
701 | |||
702 | // Probe each move. |
||
703 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
704 | Move move = rootMoves[i].pv[0]; |
||
705 | pos.do_move(move, st, pos.gives_check(move, ci)); |
||
706 | int v = 0; |
||
707 | if (pos.checkers() && dtz > 0) { |
||
708 | ExtMove s[192]; |
||
709 | if (generate<LEGAL>(pos, s) == s) |
||
710 | v = 1; |
||
711 | } |
||
712 | if (!v) { |
||
713 | if (st.rule50 != 0) { |
||
714 | v = -Tablebases::probe_dtz(pos, &success); |
||
715 | if (v > 0) v++; |
||
716 | else if (v < 0) v--; |
||
717 | } else { |
||
718 | v = -Tablebases::probe_wdl(pos, &success); |
||
719 | v = wdl_to_dtz[v + 2]; |
||
720 | } |
||
721 | } |
||
722 | pos.undo_move(move); |
||
723 | if (!success) return false; |
||
724 | rootMoves[i].score = (Value)v; |
||
725 | } |
||
726 | |||
727 | // Obtain 50-move counter for the root position. |
||
728 | // In Stockfish there seems to be no clean way, so we do it like this: |
||
729 | int cnt50 = st.previous->rule50; |
||
730 | |||
731 | // Use 50-move counter to determine whether the root position is |
||
732 | // won, lost or drawn. |
||
733 | int wdl = 0; |
||
734 | if (dtz > 0) |
||
735 | wdl = (dtz + cnt50 <= 100) ? 2 : 1; |
||
736 | else if (dtz < 0) |
||
737 | wdl = (-dtz + cnt50 <= 100) ? -2 : -1; |
||
738 | |||
739 | // Determine the score to report to the user. |
||
740 | score = wdl_to_Value[wdl + 2]; |
||
741 | // If the position is winning or losing, but too few moves left, adjust the |
||
742 | // score to show how close it is to winning or losing. |
||
743 | // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci(). |
||
744 | if (wdl == 1 && dtz <= 100) |
||
745 | score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); |
||
746 | else if (wdl == -1 && dtz >= -100) |
||
747 | score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200); |
||
748 | |||
749 | // Now be a bit smart about filtering out moves. |
||
750 | size_t j = 0; |
||
751 | if (dtz > 0) { // winning (or 50-move rule draw) |
||
752 | int best = 0xffff; |
||
753 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
754 | int v = rootMoves[i].score; |
||
755 | if (v > 0 && v < best) |
||
756 | best = v; |
||
757 | } |
||
758 | int max = best; |
||
759 | // If the current phase has not seen repetitions, then try all moves |
||
760 | // that stay safely within the 50-move budget, if there are any. |
||
761 | if (!has_repeated(st.previous) && best + cnt50 <= 99) |
||
762 | max = 99 - cnt50; |
||
763 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
764 | int v = rootMoves[i].score; |
||
765 | if (v > 0 && v <= max) |
||
766 | rootMoves[j++] = rootMoves[i]; |
||
767 | } |
||
768 | } else if (dtz < 0) { // losing (or 50-move rule draw) |
||
769 | int best = 0; |
||
770 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
771 | int v = rootMoves[i].score; |
||
772 | if (v < best) |
||
773 | best = v; |
||
774 | } |
||
775 | // Try all moves, unless we approach or have a 50-move rule draw. |
||
776 | if (-best * 2 + cnt50 < 100) |
||
777 | return true; |
||
778 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
779 | if (rootMoves[i].score == best) |
||
780 | rootMoves[j++] = rootMoves[i]; |
||
781 | } |
||
782 | } else { // drawing |
||
783 | // Try all moves that preserve the draw. |
||
784 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
785 | if (rootMoves[i].score == 0) |
||
786 | rootMoves[j++] = rootMoves[i]; |
||
787 | } |
||
788 | } |
||
789 | rootMoves.resize(j, Search::RootMove(MOVE_NONE)); |
||
790 | |||
791 | return true; |
||
792 | } |
||
793 | |||
794 | // Use the WDL tables to filter out moves that don't preserve the win or draw. |
||
795 | // This is a fallback for the case that some or all DTZ tables are missing. |
||
796 | // |
||
797 | // A return value false indicates that not all probes were successful and that |
||
798 | // no moves were filtered out. |
||
799 | bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score) |
||
800 | { |
||
801 | int success; |
||
802 | |||
803 | int wdl = Tablebases::probe_wdl(pos, &success); |
||
804 | if (!success) return false; |
||
805 | score = wdl_to_Value[wdl + 2]; |
||
806 | |||
807 | StateInfo st; |
||
808 | CheckInfo ci(pos); |
||
809 | |||
810 | int best = -2; |
||
811 | |||
812 | // Probe each move. |
||
813 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
814 | Move move = rootMoves[i].pv[0]; |
||
815 | pos.do_move(move, st, pos.gives_check(move, ci)); |
||
816 | int v = -Tablebases::probe_wdl(pos, &success); |
||
817 | pos.undo_move(move); |
||
818 | if (!success) return false; |
||
819 | rootMoves[i].score = (Value)v; |
||
820 | if (v > best) |
||
821 | best = v; |
||
822 | } |
||
823 | |||
824 | size_t j = 0; |
||
825 | for (size_t i = 0; i < rootMoves.size(); i++) { |
||
826 | if (rootMoves[i].score == best) |
||
827 | rootMoves[j++] = rootMoves[i]; |
||
828 | } |
||
829 | rootMoves.resize(j, Search::RootMove(MOVE_NONE)); |
||
830 | |||
831 | return true; |
||
832 | } |
||
833 |