Subversion Repositories Games.Chess Giants

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 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
#include "../piece.hpp"
11
#include "../position.hpp"
12
#include "../moveGen.hpp"
13
 
14
#include <type_traits>
15
 
16
#include "rtb-probe.hpp"
17
#include "rtb-core.hpp"
18
 
19
#include "rtb-core-impl.hpp"
20
 
21
int Syzygy::TBLargest = 0;
22
 
23
// Given a position with 6 or fewer pieces, produce a text string
24
// of the form KQPvKRP, where "KQP" represents the white pieces if
25
// mirror == false and the black pieces if mirror == true.
26
static void prt_str(Position& pos, char *str, bool mirror)
27
{
28
    static_assert(Piece::WQUEEN  == Piece::WKING   + 1, "");
29
    static_assert(Piece::WROOK   == Piece::WQUEEN  + 1, "");
30
    static_assert(Piece::WBISHOP == Piece::WROOK   + 1, "");
31
    static_assert(Piece::WKNIGHT == Piece::WBISHOP + 1, "");
32
    static_assert(Piece::WPAWN   == Piece::WKNIGHT + 1, "");
33
    static_assert(Piece::BQUEEN  == Piece::BKING   + 1, "");
34
    static_assert(Piece::BROOK   == Piece::BQUEEN  + 1, "");
35
    static_assert(Piece::BBISHOP == Piece::BROOK   + 1, "");
36
    static_assert(Piece::BKNIGHT == Piece::BBISHOP + 1, "");
37
    static_assert(Piece::BPAWN   == Piece::BKNIGHT + 1, "");
38
 
39
    static char pchr[Piece::nPieceTypes+1] = " KQRBNPKQRBNP";
40
 
41
    int p1Beg = mirror ? Piece::BKING : Piece::WKING;
42
    int p1End = mirror ? Piece::BPAWN : Piece::WPAWN;
43
    int p2Beg = mirror ? Piece::WKING : Piece::BKING;
44
    int p2End = mirror ? Piece::WPAWN : Piece::BPAWN;
45
 
46
    for (int p = p1Beg; p <= p1End; p++) {
47
        int cnt = BitBoard::bitCount(pos.pieceTypeBB((Piece::Type)p));
48
        for (int i = 0; i < cnt; i++)
49
            *str++ = pchr[p];
50
    }
51
    *str++ = 'v';
52
    for (int p = p2Beg; p <= p2End; p++) {
53
        int cnt = BitBoard::bitCount(pos.pieceTypeBB((Piece::Type)p));
54
        for (int i = 0; i < cnt; i++)
55
            *str++ = pchr[p];
56
    }
57
    *str++ = 0;
58
}
59
 
60
// Given a position, produce a 64-bit material signature key.
61
// If the engine supports such a key, it should equal the engine's key.
62
static uint64_t calc_key(const Position& pos, bool mirror)
63
{
64
    uint64_t h = mirror ? MatId::mirror(pos.materialId()) : pos.materialId();
65
    h *= 0x842c2f50a7ac0ae1ULL;
66
    h = ((h >> 32) ^ h) * 0xace7b66dbad28265ULL;
67
    return h;
68
}
69
 
70
// Produce a 64-bit material key corresponding to the material combination
71
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
72
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
73
// pawns, ..., kings.
74
static uint64_t calc_key_from_pcs(const int *pcs, bool mirror)
75
{
76
    MatId key;
77
    key.addPieceCnt(Piece::WPAWN,   pcs[1]);
78
    key.addPieceCnt(Piece::WKNIGHT, pcs[2]);
79
    key.addPieceCnt(Piece::WBISHOP, pcs[3]);
80
    key.addPieceCnt(Piece::WROOK,   pcs[4]);
81
    key.addPieceCnt(Piece::WQUEEN,  pcs[5]);
82
    key.addPieceCnt(Piece::BPAWN,   pcs[8+1]);
83
    key.addPieceCnt(Piece::BKNIGHT, pcs[8+2]);
84
    key.addPieceCnt(Piece::BBISHOP, pcs[8+3]);
85
    key.addPieceCnt(Piece::BROOK,   pcs[8+4]);
86
    key.addPieceCnt(Piece::BQUEEN,  pcs[8+5]);
87
 
88
    uint64_t h = mirror ? MatId::mirror(key()) : key();
89
    h *= 0x842c2f50a7ac0ae1ULL;
90
    h = ((h >> 32) ^ h) * 0xace7b66dbad28265ULL;
91
    return h;
92
}
93
 
94
static uint64_t get_pieces(const Position& pos, int color, int piece) {
95
    int p = 7 - piece;
96
    if (color)
97
        p += Piece::BKING - Piece::WKING;
98
    return pos.pieceTypeBB((Piece::Type)p);
99
}
100
 
101
static inline int pop_lsb(uint64_t& bb) {
102
    return BitBoard::extractSquare(bb);
103
}
104
 
105
// probe_wdl_table and probe_dtz_table require similar adaptations.
106
static int probe_wdl_table(Position& pos, int *success)
107
{
108
    struct TBEntry *ptr;
109
    struct TBHashEntry *ptr2;
110
    uint64_t idx;
111
    uint64_t key;
112
    int i;
113
    ubyte res;
114
    int p[TBPIECES];
115
 
116
    // Obtain the position's material signature key.
117
    key = calc_key(pos, false);
118
 
119
    // Test for KvK.
120
    if (!key) return 0;
121
 
122
    ptr2 = WDL_hash[key >> (64 - TBHASHBITS)];
123
    for (i = 0; i < HSHMAX; i++)
124
        if (ptr2[i].key == key) break;
125
    if (i == HSHMAX) {
126
        *success = 0;
127
        return 0;
128
    }
129
 
130
    ptr = ptr2[i].ptr;
131
    ubyte ready = ptr->ready.load(std::memory_order_relaxed);
132
    std::atomic_thread_fence(std::memory_order_acquire);
133
    if (!ready) {
134
        std::lock_guard<std::mutex> L(TB_mutex);
135
        ready = ptr->ready.load(std::memory_order_relaxed);
136
        if (!ready) {
137
            char str[16];
138
            prt_str(pos, str, ptr->key != key);
139
            if (!init_table_wdl(ptr, str)) {
140
                ptr2[i].key = 0ULL;
141
                *success = 0;
142
                return 0;
143
            }
144
            std::atomic_thread_fence(std::memory_order_release);
145
            ptr->ready.store(1, std::memory_order_relaxed);
146
        }
147
    }
148
 
149
    int bside, mirror, cmirror;
150
    if (!ptr->symmetric) {
151
        if (key != ptr->key) {
152
            cmirror = 8;
153
            mirror = 0x38;
154
            bside = pos.isWhiteMove();
155
        } else {
156
            cmirror = mirror = 0;
157
            bside = !pos.isWhiteMove();
158
        }
159
    } else {
160
        cmirror = pos.isWhiteMove() ? 0 : 8;
161
        mirror = pos.isWhiteMove() ? 0 : 0x38;
162
        bside = 0;
163
    }
164
 
165
    // p[i] is to contain the square 0-63 (A1-H8) for a piece of type
166
    // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
167
    // Pieces of the same type are guaranteed to be consecutive.
168
    if (!ptr->has_pawns) {
169
        struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
170
        ubyte *pc = entry->pieces[bside];
171
        for (i = 0; i < entry->num;) {
172
            uint64_t bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
173
            do {
174
                p[i++] = pop_lsb(bb);
175
            } while (bb);
176
        }
177
        idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
178
        res = decompress_pairs(entry->precomp[bside], idx);
179
    } else {
180
        struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
181
        int k = entry->file[0].pieces[0][0] ^ cmirror;
182
        uint64_t bb = get_pieces(pos, k >> 3, k & 0x07);
183
        i = 0;
184
        do {
185
            p[i++] = pop_lsb(bb) ^ mirror;
186
        } while (bb);
187
        int f = pawn_file(entry, p);
188
        ubyte *pc = entry->file[f].pieces[bside];
189
        for (; i < entry->num;) {
190
            bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
191
            do {
192
                p[i++] = pop_lsb(bb) ^ mirror;
193
            } while (bb);
194
        }
195
        idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
196
        res = decompress_pairs(entry->file[f].precomp[bside], idx);
197
    }
198
 
199
    return ((int)res) - 2;
200
}
201
 
202
static int probe_dtz_table(Position& pos, int wdl, int *success)
203
{
204
    uint64_t idx;
205
    int i, res;
206
    int p[TBPIECES];
207
 
208
    // Obtain the position's material signature key.
209
    uint64_t key = calc_key(pos, false);
210
 
211
    DTZTableEntry* dtzTabEnt;
212
    {
213
        dtzTabEnt = DTZ_hash[key >> (64 - TBHASHBITS)];
214
        for (i = 0; i < HSHMAX; i++)
215
            if (dtzTabEnt[i].key1 == key) break;
216
        if (i == HSHMAX) {
217
            uint64_t key2 = calc_key(pos, true);
218
            dtzTabEnt = DTZ_hash[key2 >> (64 - TBHASHBITS)];
219
            for (i = 0; i < HSHMAX; i++)
220
                if (dtzTabEnt[i].key2 == key) break;
221
        }
222
        if (i == HSHMAX) {
223
            *success = 0;
224
            return 0;
225
        }
226
        dtzTabEnt += i;
227
    }
228
 
229
    TBEntry* ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
230
    std::atomic_thread_fence(std::memory_order_acquire);
231
    if (!ptr) {
232
        std::lock_guard<std::mutex> L(TB_mutex);
233
        ptr = dtzTabEnt->entry.load(std::memory_order_relaxed);
234
        if (!ptr) {
235
            struct TBHashEntry *ptr2 = WDL_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
            char str[16];
243
            bool mirror = (ptr2[i].ptr->key != key);
244
            prt_str(pos, str, mirror);
245
            ptr = load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
246
            std::atomic_thread_fence(std::memory_order_release);
247
            dtzTabEnt->entry.store(ptr, std::memory_order_relaxed);
248
        }
249
    }
250
 
251
    if (!ptr) {
252
        *success = 0;
253
        return 0;
254
    }
255
 
256
    int bside, mirror, cmirror;
257
    if (!ptr->symmetric) {
258
        if (key != ptr->key) {
259
            cmirror = 8;
260
            mirror = 0x38;
261
            bside = pos.isWhiteMove();
262
        } else {
263
            cmirror = mirror = 0;
264
            bside = !pos.isWhiteMove();
265
        }
266
    } else {
267
        cmirror = pos.isWhiteMove() ? 0 : 8;
268
        mirror = pos.isWhiteMove() ? 0 : 0x38;
269
        bside = 0;
270
    }
271
 
272
    if (!ptr->has_pawns) {
273
        struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
274
        if ((entry->flags & 1) != bside && !entry->symmetric) {
275
            *success = -1;
276
            return 0;
277
        }
278
        ubyte *pc = entry->pieces;
279
        for (i = 0; i < entry->num;) {
280
            uint64_t bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
281
            do {
282
                p[i++] = pop_lsb(bb);
283
            } while (bb);
284
        }
285
        idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
286
        res = decompress_pairs(entry->precomp, idx);
287
 
288
        if (entry->flags & 2)
289
            res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
290
 
291
        if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
292
            res *= 2;
293
    } else {
294
        struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
295
        int k = entry->file[0].pieces[0] ^ cmirror;
296
        uint64_t bb = get_pieces(pos, k >> 3, k & 0x07);
297
        i = 0;
298
        do {
299
            p[i++] = pop_lsb(bb) ^ mirror;
300
        } while (bb);
301
        int f = pawn_file((struct TBEntry_pawn *)entry, p);
302
        if ((entry->flags[f] & 1) != bside) {
303
            *success = -1;
304
            return 0;
305
        }
306
        ubyte *pc = entry->file[f].pieces;
307
        for (; i < entry->num;) {
308
            bb = get_pieces(pos, (pc[i] ^ cmirror) >> 3, pc[i] & 0x07);
309
            do {
310
                p[i++] = pop_lsb(bb) ^ mirror;
311
            } while (bb);
312
        }
313
        idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
314
        res = decompress_pairs(entry->file[f].precomp, idx);
315
 
316
        if (entry->flags[f] & 2)
317
            res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
318
 
319
        if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
320
            res *= 2;
321
    }
322
 
323
    return res;
324
}
325
 
326
// Add bishop and rook underpromotion captures to move list.
327
static void add_underprom_caps(Position& pos, MoveList& moveList)
328
{
329
    const int nMoves = moveList.size;
330
    const bool wtm = pos.isWhiteMove();
331
    const int queen = wtm ? Piece::WQUEEN : Piece::BQUEEN;
332
    for (int i = 0; i < nMoves; i++) {
333
        const Move& m = moveList[i];
334
        if ((m.promoteTo() == queen) && (pos.getPiece(m.to()) != Piece::EMPTY)) {
335
            moveList.addMove(m.from(), m.to(), wtm ? Piece::WROOK : Piece::BROOK);
336
            moveList.addMove(m.from(), m.to(), wtm ? Piece::WBISHOP : Piece::BBISHOP);
337
        }
338
    }
339
}
340
 
341
static int probe_ab(Position& pos, int alpha, int beta, int *success)
342
{
343
    // Generate (at least) all legal non-ep captures including (under)promotions.
344
    // It is OK to generate more, as long as they are filtered out below.
345
    MoveList moveList;
346
    const bool inCheck = MoveGen::inCheck(pos);
347
    if (inCheck) {
348
        MoveGen::checkEvasions(pos, moveList);
349
    } else {
350
        MoveGen::pseudoLegalCaptures(pos, moveList);
351
        // Since bishop and rook promotions are not included, we need to add them.
352
        add_underprom_caps(pos, moveList);
353
    }
354
 
355
    UndoInfo ui;
356
    for (int m = 0; m < moveList.size; m++) {
357
        const Move& capture = moveList[m];
358
        if ((pos.getPiece(capture.to()) == Piece::EMPTY) ||
359
            !MoveGen::isLegal(pos, capture, inCheck))
360
            continue;
361
        pos.makeMove(capture, ui);
362
        int v = -probe_ab(pos, -beta, -alpha, success);
363
        pos.unMakeMove(capture, ui);
364
        if (*success == 0) return 0;
365
        if (v > alpha) {
366
            if (v >= beta) {
367
                *success = 2;
368
                return v;
369
            }
370
            alpha = v;
371
        }
372
    }
373
 
374
    int v = probe_wdl_table(pos, success);
375
    if (*success == 0) return 0;
376
    if (alpha >= v) {
377
        *success = 1 + (alpha > 0);
378
        return alpha;
379
    } else {
380
        *success = 1;
381
        return v;
382
    }
383
}
384
 
385
int Syzygy::probe_wdl(Position& pos, int *success)
386
{
387
    *success = 1;
388
    int v = probe_ab(pos, -2, 2, success);
389
 
390
    // If en passant is not possible, we are done.
391
    if (pos.getEpSquare() == -1)
392
        return v;
393
    if (!(*success)) return 0;
394
 
395
    // Now handle en passant.
396
    int v1 = -3;
397
    // Generate (at least) all legal en passant captures.
398
    MoveList moveList;
399
 
400
    const bool inCheck = MoveGen::inCheck(pos);
401
    if (inCheck) {
402
        MoveGen::checkEvasions(pos, moveList);
403
    } else {
404
        MoveGen::pseudoLegalMoves(pos, moveList);
405
    }
406
 
407
    const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
408
    UndoInfo ui;
409
    for (int m = 0; m < moveList.size; m++) {
410
        const Move& capture = moveList[m];
411
        if ((capture.to() != pos.getEpSquare()) || (pos.getPiece(capture.from()) != pawn) ||
412
            !MoveGen::isLegal(pos, capture, inCheck))
413
            continue;
414
        pos.makeMove(capture, ui);
415
        int v0 = -probe_ab(pos, -2, 2, success);
416
        pos.unMakeMove(capture, ui);
417
        if (*success == 0) return 0;
418
        if (v0 > v1) v1 = v0;
419
    }
420
    if (v1 > -3) {
421
        if (v1 >= v) v = v1;
422
        else if (v == 0) {
423
            // Check whether there is at least one legal non-ep move.
424
            for (int m = 0; m < moveList.size; m++) {
425
                const Move& capture = moveList[m];
426
                if ((capture.to() == pos.getEpSquare()) &&
427
                    (pos.getPiece(capture.from()) == pawn))
428
                    continue;
429
                if (MoveGen::isLegal(pos, capture, inCheck))
430
                    return v;
431
            }
432
            // If not, then we are forced to play the losing ep capture.
433
            v = v1;
434
        }
435
    }
436
 
437
    return v;
438
}
439
 
440
// This routine treats a position with en passant captures as one without.
441
static int probe_dtz_no_ep(Position& pos, int *success)
442
{
443
    const int wdl = probe_ab(pos, -2, 2, success);
444
    if (*success == 0) return 0;
445
 
446
    if (wdl == 0) return 0;
447
 
448
    if (*success == 2)
449
        return wdl == 2 ? 1 : 101;
450
 
451
    MoveList moveList;
452
    const bool inCheck = MoveGen::inCheck(pos);
453
    const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
454
    UndoInfo ui;
455
 
456
    if (wdl > 0) {
457
        // Generate at least all legal non-capturing pawn moves
458
        // including non-capturing promotions.
459
        if (inCheck) {
460
            MoveGen::checkEvasions(pos, moveList);
461
        } else {
462
            MoveGen::pseudoLegalMoves(pos, moveList);
463
        }
464
 
465
        for (int m = 0; m < moveList.size; m++) {
466
            const Move& move = moveList[m];
467
            if ((pos.getPiece(move.from()) != pawn) ||
468
                (Position::getX(move.from()) != Position::getX(move.to())) ||
469
                !MoveGen::isLegal(pos, move, inCheck))
470
                continue;
471
            pos.makeMove(move, ui);
472
            int v = -probe_ab(pos, -2, -wdl + 1, success);
473
            pos.unMakeMove(move, ui);
474
            if (*success == 0) return 0;
475
            if (v == wdl)
476
                return v == 2 ? 1 : 101;
477
        }
478
    }
479
 
480
    int dtz = 1 + probe_dtz_table(pos, wdl, success);
481
 
482
    if (*success >= 0) {
483
        if (wdl & 1) dtz += 100;
484
        return wdl >= 0 ? dtz : -dtz;
485
    }
486
 
487
    if (wdl > 0) {
488
        int best = 0xffff;
489
        for (int m = 0; m < moveList.size; m++) {
490
            const Move& move = moveList[m];
491
            if ((pos.getPiece(move.to()) != Piece::EMPTY) ||
492
                (pos.getPiece(move.from()) == pawn) ||
493
                !MoveGen::isLegal(pos, move, inCheck))
494
                continue;
495
            pos.makeMove(move, ui);
496
            int v = -Syzygy::probe_dtz(pos, success);
497
            pos.unMakeMove(move, ui);
498
            if (*success == 0) return 0;
499
            if (v > 0 && v + 1 < best)
500
                best = v + 1;
501
        }
502
        return best;
503
    } else {
504
        int best = -1;
505
        if (inCheck) {
506
            MoveGen::checkEvasions(pos, moveList);
507
        } else {
508
            MoveGen::pseudoLegalMoves(pos, moveList);
509
        }
510
        for (int m = 0; m < moveList.size; m++) {
511
            const Move& move = moveList[m];
512
            if (!MoveGen::isLegal(pos, move, inCheck))
513
                continue;
514
            pos.makeMove(move, ui);
515
            int v;
516
            if (pos.getHalfMoveClock() == 0) {
517
                if (wdl == -2) v = -1;
518
                else {
519
                    v = probe_ab(pos, 1, 2, success);
520
                    v = (v == 2) ? 0 : -101;
521
                }
522
            } else {
523
                v = -Syzygy::probe_dtz(pos, success) - 1;
524
            }
525
            pos.unMakeMove(move, ui);
526
            if (*success == 0) return 0;
527
            if (v < best)
528
                best = v;
529
        }
530
        return best;
531
    }
532
}
533
 
534
static int wdl_to_dtz[] = {
535
    -1, -101, 0, 101, 1
536
};
537
 
538
int Syzygy::probe_dtz(Position& pos, int *success)
539
{
540
    *success = 1;
541
    int v = probe_dtz_no_ep(pos, success);
542
 
543
    if (pos.getEpSquare() == -1)
544
        return v;
545
    if (*success == 0) return 0;
546
 
547
    // Now handle en passant.
548
    int v1 = -3;
549
 
550
    MoveList moveList;
551
    const bool inCheck = MoveGen::inCheck(pos);
552
    const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
553
    UndoInfo ui;
554
 
555
    if (!inCheck) {
556
        MoveGen::pseudoLegalMoves(pos, moveList);
557
    } else {
558
        MoveGen::checkEvasions(pos, moveList);
559
    }
560
 
561
    for (int m = 0; m < moveList.size; m++) {
562
        const Move& capture = moveList[m];
563
        if ((capture.to() != pos.getEpSquare()) ||
564
            (pos.getPiece(capture.from()) != pawn) ||
565
            !MoveGen::isLegal(pos, capture, inCheck))
566
            continue;
567
        pos.makeMove(capture, ui);
568
        int v0 = -probe_ab(pos, -2, 2, success);
569
        pos.unMakeMove(capture, ui);
570
        if (*success == 0) return 0;
571
        if (v0 > v1) v1 = v0;
572
    }
573
    if (v1 > -3) {
574
        v1 = wdl_to_dtz[v1 + 2];
575
        if (v < -100) {
576
            if (v1 >= 0)
577
                v = v1;
578
        } else if (v < 0) {
579
            if (v1 >= 0 || v1 < 100)
580
                v = v1;
581
        } else if (v > 100) {
582
            if (v1 > 0)
583
                v = v1;
584
        } else if (v > 0) {
585
            if (v1 == 1)
586
                v = v1;
587
        } else if (v1 >= 0) {
588
            v = v1;
589
        } else {
590
            for (int m = 0; m < moveList.size; m++) {
591
                const Move& move = moveList[m];
592
                if ((move.to() == pos.getEpSquare()) && (pos.getPiece(move.from()) == pawn))
593
                    continue;
594
                if (MoveGen::isLegal(pos, move, inCheck))
595
                    return v;
596
            }
597
            v = v1;
598
        }
599
    }
600
 
601
    return v;
602
}