/*
 
 * tbprobe.c
 
 * Copyright (c) 2013-2015 Ronald de Man
 
 * This file may be redistributed and/or modified without restrictions.
 
 *
 
 * (C) 2015 basil, all rights reserved,
 
 * Permission is hereby granted, free of charge, to any person obtaining a
 
 * copy of this software and associated documentation files (the "Software"),
 
 * to deal in the Software without restriction, including without limitation
 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
 * and/or sell copies of the Software, and to permit persons to whom the
 
 * Software is furnished to do so, subject to the following conditions:
 
 * 
 
 * The above copyright notice and this permission notice shall be included in
 
 * all copies or substantial portions of the Software.
 
 * 
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
 * DEALINGS IN THE SOFTWARE.
 
 */
 
 
 
#include <assert.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
 
 
#include "tbprobe.h"
 
 
 
//#include <x86intrin.h>
 
 
 
#define WHITE_KING              (TB_WPAWN + 5)
 
#define WHITE_QUEEN             (TB_WPAWN + 4)
 
#define WHITE_ROOK              (TB_WPAWN + 3)
 
#define WHITE_BISHOP            (TB_WPAWN + 2)
 
#define WHITE_KNIGHT            (TB_WPAWN + 1)
 
#define WHITE_PAWN              TB_WPAWN
 
#define BLACK_KING              (TB_BPAWN + 5)
 
#define BLACK_QUEEN             (TB_BPAWN + 4)
 
#define BLACK_ROOK              (TB_BPAWN + 3)
 
#define BLACK_BISHOP            (TB_BPAWN + 2)
 
#define BLACK_KNIGHT            (TB_BPAWN + 1)
 
#define BLACK_PAWN              TB_BPAWN
 
 
 
#define PRIME_WHITE_QUEEN       11811845319353239651ull
 
#define PRIME_WHITE_ROOK        10979190538029446137ull
 
#define PRIME_WHITE_BISHOP      12311744257139811149ull
 
#define PRIME_WHITE_KNIGHT      15202887380319082783ull
 
#define PRIME_WHITE_PAWN        17008651141875982339ull
 
#define PRIME_BLACK_QUEEN       15484752644942473553ull
 
#define PRIME_BLACK_ROOK        18264461213049635989ull
 
#define PRIME_BLACK_BISHOP      15394650811035483107ull
 
#define PRIME_BLACK_KNIGHT      13469005675588064321ull
 
#define PRIME_BLACK_PAWN        11695583624105689831ull
 
 
 
#define BOARD_RANK_EDGE         0x8181818181818181ull
 
#define BOARD_FILE_EDGE         0xFF000000000000FFull
 
#define BOARD_EDGE              (BOARD_RANK_EDGE | BOARD_FILE_EDGE)
 
#define BOARD_RANK_1            0x00000000000000FFull
 
#define BOARD_FILE_A            0x8080808080808080ull
 
 
 
#define KEY_KvK                 0
 
 
 
#define BEST_NONE               0xFFFF
 
#define SCORE_ILLEGAL           0x7FFF
 
 
 
#define popcount(v) PopCnt(v)
 
 
 
#define poplsb(x)               ((x) & ((x) - 1))
 
 
 
#define make_move(promote, from, to)                                    \
 
    ((((promote) & 0x7) << 12) | (((from) & 0x3F) << 6) | ((to) & 0x3F))
 
#define move_from(move)                                                 \
 
    (((move) >> 6) & 0x3F)
 
#define move_to(move)                                                   \
 
    ((move) & 0x3F)
 
#define move_promotes(move)                                             \
 
    (((move) >> 12) & 0x7)
 
 
 
#define MAX_MOVES               TB_MAX_MOVES
 
#define MOVE_STALEMATE          0xFFFF
 
#define MOVE_CHECKMATE          0xFFFE
 
 
 
struct pos {
 
  uint64_t white;
 
  uint64_t black;
 
  uint64_t kings;
 
  uint64_t queens;
 
  uint64_t rooks;
 
  uint64_t bishops;
 
  uint64_t knights;
 
  uint64_t pawns;
 
  uint8_t rule50;
 
  uint8_t ep;
 
  bool turn;
 
};
 
 
 
static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move);
 
static int probe_dtz(const struct pos *pos, int *success);
 
 
 
unsigned TB_LARGEST = 0;
 
#include "tbcore.c"
 
 
 
#define rank(s)                 ((s) >> 3)
 
#define file(s)                 ((s) & 0x07)
 
#define board(s)                ((uint64_t)1 << (s))
 
static inline unsigned _lsb(uint64_t b) {
 
#ifdef _WIN32 // Pierre-Marie Baty -- Win32 addition
 
  static const int index64[64] = {
 
     0, 47,  1, 56, 48, 27,  2, 60,
 
     57, 49, 41, 37, 28, 16,  3, 61,
 
     54, 58, 35, 52, 50, 42, 21, 44,
 
     38, 32, 29, 23, 17, 11,  4, 62,
 
     46, 55, 26, 59, 40, 36, 15, 53,
 
     34, 51, 20, 43, 31, 22, 10, 45,
 
     25, 39, 14, 33, 19, 30,  9, 24,
 
     13, 18,  8, 12,  7,  6,  5, 63
 
  };
 
  /**
 
  * bitScanForward
 
  * @author Kim Walisch (2012)
 
  * @param bb bitboard to scan
 
  * @precondition bb != 0
 
  * @return index (0..63) of least significant one bit
 
  */
 
   const uint64_t debruijn64 = 0x03f79d71b4cb0a89ULL;
 
   return index64[((b ^ (b - 1)) * debruijn64) >> 58];
 
#else
 
  size_t idx;
 
__asm__("bsfq %1, %0": "=r"(idx):"rm"(b));
 
  return idx;
 
#endif
 
}
 
 
 
#define square(r, f)            (8 * (r) + (f))
 
 
 
#ifdef TB_KING_ATTACKS
 
#  define king_attacks(s)         TB_KING_ATTACKS(s)
 
#  define king_attacks_init()
 
                            /* NOP */
 
#else                           /* TB_KING_ATTACKS */
 
 
 
static uint64_t king_attacks_table[64];
 
 
 
#  define king_attacks(s)         king_attacks_table[(s)]
 
 
 
static void king_attacks_init(void) {
 
  for (unsigned s = 0; s < 64; s++) {
 
    unsigned r = rank(s);
 
    unsigned f = file(s);
 
    uint64_t b = 0;
 
    if (r != 0 && f != 0)
 
      b |= board(square(r - 1, f - 1));
 
    if (r != 0)
 
      b |= board(square(r - 1, f));
 
    if (r != 0 && f != 7)
 
      b |= board(square(r - 1, f + 1));
 
    if (f != 7)
 
      b |= board(square(r, f + 1));
 
    if (r != 7 && f != 7)
 
      b |= board(square(r + 1, f + 1));
 
    if (r != 7)
 
      b |= board(square(r + 1, f));
 
    if (r != 7 && f != 0)
 
      b |= board(square(r + 1, f - 1));
 
    if (f != 0)
 
      b |= board(square(r, f - 1));
 
    king_attacks_table[s] = b;
 
  }
 
}
 
 
 
#endif                          /* TB_KING_ATTACKS */
 
 
 
#ifdef TB_KNIGHT_ATTACKS
 
#  define knight_attacks(s)       TB_KNIGHT_ATTACKS(s)
 
#  define knight_attacks_init()
 
                              /* NOP */
 
#else                           /* TB_KNIGHT_ATTACKS */
 
 
 
static uint64_t knight_attacks_table[64];
 
 
 
#  define knight_attacks(s)       knight_attacks_table[(s)]
 
 
 
static void knight_attacks_init(void) {
 
  for (unsigned s = 0; s < 64; s++) {
 
    int r1, r = rank(s);
 
    int f1, f = file(s);
 
    uint64_t b = 0;
 
    r1 = r - 1;
 
    f1 = f - 2;
 
    if (r1 >= 0 && f1 >= 0)
 
      b |= board(square(r1, f1));
 
    r1 = r - 1;
 
    f1 = f + 2;
 
    if (r1 >= 0 && f1 <= 7)
 
      b |= board(square(r1, f1));
 
    r1 = r - 2;
 
    f1 = f - 1;
 
    if (r1 >= 0 && f1 >= 0)
 
      b |= board(square(r1, f1));
 
    r1 = r - 2;
 
    f1 = f + 1;
 
    if (r1 >= 0 && f1 <= 7)
 
      b |= board(square(r1, f1));
 
    r1 = r + 1;
 
    f1 = f - 2;
 
    if (r1 <= 7 && f1 >= 0)
 
      b |= board(square(r1, f1));
 
    r1 = r + 1;
 
    f1 = f + 2;
 
    if (r1 <= 7 && f1 <= 7)
 
      b |= board(square(r1, f1));
 
    r1 = r + 2;
 
    f1 = f - 1;
 
    if (r1 <= 7 && f1 >= 0)
 
      b |= board(square(r1, f1));
 
    r1 = r + 2;
 
    f1 = f + 1;
 
    if (r1 <= 7 && f1 <= 7)
 
      b |= board(square(r1, f1));
 
    knight_attacks_table[s] = b;
 
  }
 
}
 
 
 
#endif                          /* TB_KNIGHT_ATTACKS */
 
 
 
#ifdef TB_BISHOP_ATTACKS
 
#  define bishop_attacks(s, occ)  TB_BISHOP_ATTACKS(s, occ)
 
#  define bishop_attacks_init()
 
                              /* NOP */
 
#else                           /* TB_BISHOP_ATTACKS */
 
 
 
static uint64_t diag_attacks_table[64][64];
 
static uint64_t anti_attacks_table[64][64];
 
 
 
static const unsigned square2diag_table[64] = {
 
  0, 1, 2, 3, 4, 5, 6, 7,
 
  14, 0, 1, 2, 3, 4, 5, 6,
 
  13, 14, 0, 1, 2, 3, 4, 5,
 
  12, 13, 14, 0, 1, 2, 3, 4,
 
  11, 12, 13, 14, 0, 1, 2, 3,
 
  10, 11, 12, 13, 14, 0, 1, 2,
 
  9, 10, 11, 12, 13, 14, 0, 1,
 
  8, 9, 10, 11, 12, 13, 14, 0
 
};
 
 
 
static const unsigned square2anti_table[64] = {
 
  8, 9, 10, 11, 12, 13, 14, 0,
 
  9, 10, 11, 12, 13, 14, 0, 1,
 
  10, 11, 12, 13, 14, 0, 1, 2,
 
  11, 12, 13, 14, 0, 1, 2, 3,
 
  12, 13, 14, 0, 1, 2, 3, 4,
 
  13, 14, 0, 1, 2, 3, 4, 5,
 
  14, 0, 1, 2, 3, 4, 5, 6,
 
  0, 1, 2, 3, 4, 5, 6, 7
 
};
 
 
 
static const uint64_t diag2board_table[15] = {
 
  0x8040201008040201ull,
 
  0x0080402010080402ull,
 
  0x0000804020100804ull,
 
  0x0000008040201008ull,
 
  0x0000000080402010ull,
 
  0x0000000000804020ull,
 
  0x0000000000008040ull,
 
  0x0000000000000080ull,
 
  0x0100000000000000ull,
 
  0x0201000000000000ull,
 
  0x0402010000000000ull,
 
  0x0804020100000000ull,
 
  0x1008040201000000ull,
 
  0x2010080402010000ull,
 
  0x4020100804020100ull,
 
};
 
 
 
static const uint64_t anti2board_table[15] = {
 
  0x0102040810204080ull,
 
  0x0204081020408000ull,
 
  0x0408102040800000ull,
 
  0x0810204080000000ull,
 
  0x1020408000000000ull,
 
  0x2040800000000000ull,
 
  0x4080000000000000ull,
 
  0x8000000000000000ull,
 
  0x0000000000000001ull,
 
  0x0000000000000102ull,
 
  0x0000000000010204ull,
 
  0x0000000001020408ull,
 
  0x0000000102040810ull,
 
  0x0000010204081020ull,
 
  0x0001020408102040ull,
 
};
 
 
 
static inline size_t diag2index(uint64_t b, unsigned d) {
 
  b *= 0x0101010101010101ull;
 
  b >>= 56;
 
  b >>= 1;
 
  return (size_t) b;
 
}
 
 
 
static inline size_t anti2index(uint64_t b, unsigned a) {
 
  return diag2index(b, a);
 
}
 
 
 
#  define diag(s)                 square2diag_table[(s)]
 
#  define anti(s)                 square2anti_table[(s)]
 
#  define diag2board(d)           diag2board_table[(d)]
 
#  define anti2board(a)           anti2board_table[(a)]
 
 
 
static uint64_t bishop_attacks(unsigned sq, uint64_t occ) {
 
  occ &= ~board(sq);
 
  unsigned d = diag(sq), a = anti(sq);
 
  uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE);
 
  uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE);
 
  size_t d_idx = diag2index(d_occ, d);
 
  size_t a_idx = anti2index(a_occ, a);
 
  uint64_t d_attacks = diag_attacks_table[sq][d_idx];
 
  uint64_t a_attacks = anti_attacks_table[sq][a_idx];
 
  return d_attacks | a_attacks;
 
}
 
 
 
static void bishop_attacks_init(void) {
 
  for (unsigned idx = 0; idx < 64; idx++) {
 
    unsigned idx1 = idx << 1;
 
    for (unsigned s = 0; s < 64; s++) {
 
      int r = rank(s);
 
      int f = file(s);
 
      uint64_t b = 0;
 
      for (int i = -1; f + i >= 0 && r + i >= 0; i--) {
 
        unsigned occ = (1 << (f + i));
 
        b |= board(square(r + i, f + i));
 
        if (idx1 & occ)
 
          break;
 
      }
 
      for (int i = 1; f + i <= 7 && r + i <= 7; i++) {
 
        unsigned occ = (1 << (f + i));
 
        b |= board(square(r + i, f + i));
 
        if (idx1 & occ)
 
          break;
 
      }
 
      diag_attacks_table[s][idx] = b;
 
    }
 
  }
 
 
 
  for (unsigned idx = 0; idx < 64; idx++) {
 
    unsigned idx1 = idx << 1;
 
    for (unsigned s = 0; s < 64; s++) {
 
      int r = rank(s);
 
      int f = file(s);
 
      uint64_t b = 0;
 
      for (int i = -1; f + i >= 0 && r - i <= 7; i--) {
 
        unsigned occ = (1 << (f + i));
 
        b |= board(square(r - i, f + i));
 
        if (idx1 & occ)
 
          break;
 
      }
 
      for (int i = 1; f + i <= 7 && r - i >= 0; i++) {
 
        unsigned occ = (1 << (f + i));
 
        b |= board(square(r - i, f + i));
 
        if (idx1 & occ)
 
          break;
 
      }
 
      anti_attacks_table[s][idx] = b;
 
    }
 
  }
 
}
 
 
 
#endif                          /* TB_BISHOP_ATTACKS */
 
 
 
#ifdef TB_ROOK_ATTACKS
 
#  define rook_attacks(s, occ)    TB_ROOK_ATTACKS(s, occ)
 
#  define rook_attacks_init()
 
                            /* NOP */
 
#else                           /* TB_ROOK_ATTACKS */
 
 
 
static uint64_t rank_attacks_table[64][64];
 
static uint64_t file_attacks_table[64][64];
 
 
 
static inline size_t rank2index(uint64_t b, unsigned r) {
 
  b >>= (8 * r);
 
  b >>= 1;
 
  return (size_t) b;
 
}
 
 
 
static inline size_t file2index(uint64_t b, unsigned f) {
 
  b >>= f;
 
  b *= 0x0102040810204080ull;
 
  b >>= 56;
 
  b >>= 1;
 
  return (size_t) b;
 
}
 
 
 
#  define rank2board(r)           (0xFFull << (8 * (r)))
 
#  define file2board(f)           (0x0101010101010101ull << (f))
 
 
 
static uint64_t rook_attacks(unsigned sq, uint64_t occ) {
 
  occ &= ~board(sq);
 
  unsigned r = rank(sq), f = file(sq);
 
  uint64_t r_occ = occ & (rank2board(r) & ~BOARD_RANK_EDGE);
 
  uint64_t f_occ = occ & (file2board(f) & ~BOARD_FILE_EDGE);
 
  size_t r_idx = rank2index(r_occ, r);
 
  size_t f_idx = file2index(f_occ, f);
 
  uint64_t r_attacks = rank_attacks_table[sq][r_idx];
 
  uint64_t f_attacks = file_attacks_table[sq][f_idx];
 
  return r_attacks | f_attacks;
 
}
 
 
 
static void rook_attacks_init(void) {
 
  for (unsigned idx = 0; idx < 64; idx++) {
 
    unsigned idx1 = idx << 1, occ;
 
    for (int f = 0; f <= 7; f++) {
 
      uint64_t b = 0;
 
      if (f > 0) {
 
        int i = f - 1;
 
        do {
 
          occ = (1 << i);
 
          b |= board(square(0, i));
 
          i--;
 
        }
 
        while (!(idx1 & occ) && i >= 0);
 
      }
 
      if (f < 7) {
 
        int i = f + 1;
 
        do {
 
          occ = (1 << i);
 
          b |= board(square(0, i));
 
          i++;
 
        }
 
        while (!(idx1 & occ) && i <= 7);
 
      }
 
      for (int r = 0; r <= 7; r++) {
 
        rank_attacks_table[square(r, f)][idx] = b;
 
        b <<= 8;
 
      }
 
    }
 
  }
 
  for (unsigned idx = 0; idx < 64; idx++) {
 
    unsigned idx1 = idx << 1, occ;
 
    for (int r = 0; r <= 7; r++) {
 
      uint64_t b = 0;
 
      if (r > 0) {
 
        int i = r - 1;
 
        do {
 
          occ = (1 << i);
 
          b |= board(square(i, 0));
 
          i--;
 
        }
 
        while (!(idx1 & occ) && i >= 0);
 
      }
 
      if (r < 7) {
 
        int i = r + 1;
 
        do {
 
          occ = (1 << i);
 
          b |= board(square(i, 0));
 
          i++;
 
        }
 
        while (!(idx1 & occ) && i <= 7);
 
      }
 
      for (int f = 0; f <= 7; f++) {
 
        file_attacks_table[square(r, f)][idx] = b;
 
        b <<= 1;
 
      }
 
    }
 
  }
 
}
 
 
 
#endif                          /* TB_ROOK_ATTACKS */
 
 
 
#ifdef TB_QUEEN_ATTACKS
 
#  define queen_attacks(s, occ)   TB_QUEEN_ATTACKS(s, occ)
 
#else                           /* TB_QUEEN_ATTACKS */
 
#  define queen_attacks(s, occ)   \
 
    (rook_attacks((s), (occ)) | bishop_attacks((s), (occ)))
 
#endif                          /* TB_QUEEN_ATTACKS */
 
 
 
#ifdef TB_PAWN_ATTACKS
 
#  define pawn_attacks(s, c)      TB_PAWN_ATTACKS(s, c)
 
#  define pawn_attacks_init()
 
                            /* NOP */
 
#else                           /* TB_PAWN_ATTACKS */
 
 
 
static uint64_t pawn_attacks_table[2][64];
 
 
 
#  define pawn_attacks(s, c)      pawn_attacks_table[(c)][(s)]
 
 
 
static void pawn_attacks_init(void) {
 
  for (unsigned s = 0; s < 64; s++) {
 
    int r = rank(s);
 
    int f = file(s);
 
 
 
    uint64_t b = 0;
 
    if (r != 7) {
 
      if (f != 0)
 
        b |= board(square(r + 1, f - 1));
 
      if (f != 7)
 
        b |= board(square(r + 1, f + 1));
 
    }
 
    pawn_attacks_table[1][s] = b;
 
 
 
    b = 0;
 
    if (r != 0) {
 
      if (f != 0)
 
        b |= board(square(r - 1, f - 1));
 
      if (f != 7)
 
        b |= board(square(r - 1, f + 1));
 
    }
 
    pawn_attacks_table[0][s] = b;
 
  }
 
}
 
 
 
#endif                          /* TB_PAWN_ATTACKS */
 
 
 
static void prt_str(const struct pos *pos, char *str, bool mirror) {
 
  uint64_t white = pos->white, black = pos->black;
 
  int i;
 
  if (mirror) {
 
    uint64_t tmp = white;
 
    white = black;
 
    black = tmp;
 
  }
 
  *str++ = 'K';
 
  for (i = popcount(white & pos->queens); i > 0; i--)
 
    *str++ = 'Q';
 
  for (i = popcount(white & pos->rooks); i > 0; i--)
 
    *str++ = 'R';
 
  for (i = popcount(white & pos->bishops); i > 0; i--)
 
    *str++ = 'B';
 
  for (i = popcount(white & pos->knights); i > 0; i--)
 
    *str++ = 'N';
 
  for (i = popcount(white & pos->pawns); i > 0; i--)
 
    *str++ = 'P';
 
  *str++ = 'v';
 
  *str++ = 'K';
 
  for (i = popcount(black & pos->queens); i > 0; i--)
 
    *str++ = 'Q';
 
  for (i = popcount(black & pos->rooks); i > 0; i--)
 
    *str++ = 'R';
 
  for (i = popcount(black & pos->bishops); i > 0; i--)
 
    *str++ = 'B';
 
  for (i = popcount(black & pos->knights); i > 0; i--)
 
    *str++ = 'N';
 
  for (i = popcount(black & pos->pawns); i > 0; i--)
 
    *str++ = 'P';
 
  *str++ = '\0';
 
}
 
 
 
/*
 
 * Given a position, produce a 64-bit material signature key.
 
 */
 
static uint64_t calc_key(const struct pos *pos, bool mirror) {
 
  uint64_t white = pos->white, black = pos->black;
 
  if (mirror) {
 
    uint64_t tmp = white;
 
    white = black;
 
    black = tmp;
 
  }
 
  return popcount(white & pos->queens) * PRIME_WHITE_QUEEN +
 
      popcount(white & pos->rooks) * PRIME_WHITE_ROOK +
 
      popcount(white & pos->bishops) * PRIME_WHITE_BISHOP +
 
      popcount(white & pos->knights) * PRIME_WHITE_KNIGHT +
 
      popcount(white & pos->pawns) * PRIME_WHITE_PAWN +
 
      popcount(black & pos->queens) * PRIME_BLACK_QUEEN +
 
      popcount(black & pos->rooks) * PRIME_BLACK_ROOK +
 
      popcount(black & pos->bishops) * PRIME_BLACK_BISHOP +
 
      popcount(black & pos->knights) * PRIME_BLACK_KNIGHT +
 
      popcount(black & pos->pawns) * PRIME_BLACK_PAWN;
 
}
 
 
 
static uint64_t calc_key_from_pcs(int *pcs, int mirror) {
 
  mirror = (mirror ? 8 : 0);
 
  return pcs[WHITE_QUEEN ^ mirror] * PRIME_WHITE_QUEEN +
 
      pcs[WHITE_ROOK ^ mirror] * PRIME_WHITE_ROOK +
 
      pcs[WHITE_BISHOP ^ mirror] * PRIME_WHITE_BISHOP +
 
      pcs[WHITE_KNIGHT ^ mirror] * PRIME_WHITE_KNIGHT +
 
      pcs[WHITE_PAWN ^ mirror] * PRIME_WHITE_PAWN +
 
      pcs[BLACK_QUEEN ^ mirror] * PRIME_BLACK_QUEEN +
 
      pcs[BLACK_ROOK ^ mirror] * PRIME_BLACK_ROOK +
 
      pcs[BLACK_BISHOP ^ mirror] * PRIME_BLACK_BISHOP +
 
      pcs[BLACK_KNIGHT ^ mirror] * PRIME_BLACK_KNIGHT +
 
      pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN;
 
}
 
 
 
static uint64_t get_pieces(const struct pos *pos, uint8_t code) {
 
  switch (code) {
 
    case WHITE_KING:
 
      return pos->kings & pos->white;
 
    case WHITE_QUEEN:
 
      return pos->queens & pos->white;
 
    case WHITE_ROOK:
 
      return pos->rooks & pos->white;
 
    case WHITE_BISHOP:
 
      return pos->bishops & pos->white;
 
    case WHITE_KNIGHT:
 
      return pos->knights & pos->white;
 
    case WHITE_PAWN:
 
      return pos->pawns & pos->white;
 
    case BLACK_KING:
 
      return pos->kings & pos->black;
 
    case BLACK_QUEEN:
 
      return pos->queens & pos->black;
 
    case BLACK_ROOK:
 
      return pos->rooks & pos->black;
 
    case BLACK_BISHOP:
 
      return pos->bishops & pos->black;
 
    case BLACK_KNIGHT:
 
      return pos->knights & pos->black;
 
    case BLACK_PAWN:
 
      return pos->pawns & pos->black;
 
    default:
 
      return 0; // Dummy.
 
  }
 
}
 
 
 
static int probe_wdl_table(const struct pos *pos, int *success) {
 
  struct TBEntry *ptr;
 
  struct TBHashEntry *ptr2;
 
  uint64_t idx;
 
  uint64_t key;
 
  int i;
 
  uint8_t res;
 
  int p[TBPIECES];
 
 
 
 // Obtain the position's material signature key.
 
  key = calc_key(pos, false);
 
 
 
 // Test for KvK.
 
  if (key == KEY_KvK)
 
    return 0;
 
 
 
  ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
 
  for (i = 0; i < HSHMAX; i++) {
 
    if (ptr2[i].key == key)
 
      break;
 
  }
 
  if (i == HSHMAX) {
 
    *success = 0;
 
    return 0;
 
  }
 
 
 
  ptr = ptr2[i].ptr;
 
  if (!ptr->ready) {
 
    LOCK(TB_MUTEX);
 
    if (!ptr->ready) {
 
      char str[16];
 
      prt_str(pos, str, ptr->key != key);
 
      if (!init_table_wdl(ptr, str)) {
 
        ptr2[i].key = 0ULL;
 
        *success = 0;
 
        UNLOCK(TB_MUTEX);
 
        return 0;
 
      }
 
     // Memory barrier to ensure ptr->ready = 1 is not reordered.
 
#ifdef _MSC_VER
 
      MemoryBarrier (); // Pierre-Marie Baty -- Win32 equivalent
 
#else
 
      __asm__ __volatile__("":::"memory");
 
#endif // _MSC_VER
 
      ptr->ready = 1;
 
    }
 
    UNLOCK(TB_MUTEX);
 
  }
 
 
 
  int bside, mirror, cmirror;
 
  if (!ptr->symmetric) {
 
    if (key != ptr->key) {
 
      cmirror = 8;
 
      mirror = 0x38;
 
      bside = pos->turn;
 
    } else {
 
      cmirror = mirror = 0;
 
      bside = !pos->turn;
 
    }
 
  } else {
 
    cmirror = (pos->turn ? 0 : 8);
 
    mirror = (pos->turn ? 0 : 0x38);
 
    bside = 0;
 
  }
 
 
 
 // p[i] is to contain the square 0-63 (A1-H8) for a piece of type
 
 // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
 
 // Pieces of the same type are guaranteed to be consecutive.
 
  if (!ptr->has_pawns) {
 
    struct TBEntry_piece *entry = (struct TBEntry_piece *) ptr;
 
    uint8_t *pc = entry->pieces[bside];
 
    for (i = 0; i < entry->num;) {
 
      uint64_t bb = get_pieces(pos, pc[i] ^ cmirror);
 
      do {
 
        p[i++] = _lsb(bb);
 
        bb = poplsb(bb);
 
      } while (bb);
 
    }
 
    idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
 
    res = decompress_pairs(entry->precomp[bside], idx);
 
  } else {
 
    struct TBEntry_pawn *entry = (struct TBEntry_pawn *) ptr;
 
    int k = entry->file[0].pieces[0][0] ^ cmirror;
 
    uint64_t bb = get_pieces(pos, k);
 
    i = 0;
 
    do {
 
      p[i++] = _lsb(bb) ^ mirror;
 
      bb = poplsb(bb);
 
    } while (bb);
 
    int f = pawn_file(entry, p);
 
    uint8_t *pc = entry->file[f].pieces[bside];
 
    for (; i < entry->num;) {
 
      bb = get_pieces(pos, pc[i] ^ cmirror);
 
      do {
 
        p[i++] = _lsb(bb) ^ mirror;
 
        bb = poplsb(bb);
 
      } while (bb);
 
    }
 
    idx =
 
        encode_pawn(entry, entry->file[f].norm[bside], p,
 
        entry->file[f].factor[bside]);
 
    res = decompress_pairs(entry->file[f].precomp[bside], idx);
 
  }
 
 
 
  return ((int) res) - 2;
 
}
 
 
 
static int probe_dtz_table(const struct pos *pos, int wdl, int *success) {
 
  struct TBEntry *ptr;
 
  uint64_t idx;
 
  int i, res;
 
  int p[TBPIECES];
 
 
 
 // Obtain the position's material signature key.
 
  uint64_t key = calc_key(pos, false);
 
 
 
  if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) {
 
    for (i = 1; i < DTZ_ENTRIES; i++) {
 
      if (DTZ_table[i].key1 == key || DTZ_table[i].key2 == key)
 
        break; //new
 
     /*if (DTZ_table[i].key1 == key)
 
        break; *///mfb old
 
    }
 
    if (i < DTZ_ENTRIES) {
 
      struct DTZTableEntry table_entry = DTZ_table[i];
 
      for (; i > 0; i--)
 
        DTZ_table[i] = DTZ_table[i - 1];
 
      DTZ_table[0] = table_entry;
 
    } else {
 
      struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
 
      for (i = 0; i < HSHMAX; i++) {
 
        if (ptr2[i].key == key)
 
          break;
 
      }
 
      if (i == HSHMAX) {
 
        *success = 0;
 
        return 0;
 
      }
 
      ptr = ptr2[i].ptr;
 
      char str[16];
 
      int mirror = (ptr->key != key);
 
      prt_str(pos, str, mirror);
 
      if (DTZ_table[DTZ_ENTRIES - 1].entry)
 
        free_dtz_entry(DTZ_table[DTZ_ENTRIES - 1].entry);
 
      for (i = DTZ_ENTRIES - 1; i > 0; i--)
 
        DTZ_table[i] = DTZ_table[i - 1];
 
      uint64_t key1 = calc_key(pos, mirror);
 
      uint64_t key2 = calc_key(pos, !mirror);
 
      load_dtz_table(str, key1, key2);
 
    }
 
  }
 
 
 
  ptr = DTZ_table[0].entry;
 
  if (!ptr) {
 
    *success = 0;
 
    return 0;
 
  }
 
 
 
  int bside, mirror, cmirror;
 
  if (!ptr->symmetric) {
 
    if (key != ptr->key) {
 
      cmirror = 8;
 
      mirror = 0x38;
 
      bside = pos->turn;
 
    } else {
 
      cmirror = mirror = 0;
 
      bside = !pos->turn;
 
    }
 
  } else {
 
    cmirror = (pos->turn ? 0 : 8);
 
    mirror = (pos->turn ? 0 : 0x38);
 
    bside = 0;
 
  }
 
 
 
  if (!ptr->has_pawns) {
 
    struct DTZEntry_piece *entry = (struct DTZEntry_piece *) ptr;
 
    if ((entry->flags & 1) != bside && !entry->symmetric) {
 
      *success = -1;
 
      return 0;
 
    }
 
    uint8_t *pc = entry->pieces;
 
    for (i = 0; i < entry->num;) {
 
      uint64_t bb = get_pieces(pos, pc[i] ^ cmirror);
 
      do {
 
        p[i++] = _lsb(bb);
 
        bb = poplsb(bb);
 
      }
 
      while (bb);
 
    }
 
    idx =
 
        encode_piece((struct TBEntry_piece *) entry, entry->norm, p,
 
        entry->factor);
 
    res = decompress_pairs(entry->precomp, idx);
 
 
 
    if (entry->flags & 2)
 
      res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
 
    if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
 
      res *= 2;
 
  } else {
 
    struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *) ptr;
 
    int k = entry->file[0].pieces[0] ^ cmirror;
 
    uint64_t bb = get_pieces(pos, k);
 
    i = 0;
 
    do {
 
      p[i++] = _lsb(bb) ^ mirror;
 
      bb = poplsb(bb);
 
    }
 
    while (bb);
 
    int f = pawn_file((struct TBEntry_pawn *) entry, p);
 
    if ((entry->flags[f] & 1) != bside) {
 
      *success = -1;
 
      return 0;
 
    }
 
    uint8_t *pc = entry->file[f].pieces;
 
    for (; i < entry->num;) {
 
      bb = get_pieces(pos, pc[i] ^ cmirror);
 
      do {
 
        p[i++] = _lsb(bb) ^ mirror;
 
        bb = poplsb(bb);
 
      }
 
      while (bb);
 
    }
 
    idx =
 
        encode_pawn((struct TBEntry_pawn *) entry, entry->file[f].norm, p,
 
        entry->file[f].factor);
 
    res = decompress_pairs(entry->file[f].precomp, idx);
 
 
 
    if (entry->flags[f] & 2)
 
      res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
 
    if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
 
      res *= 2;
 
  }
 
 
 
  return res;
 
}
 
 
 
static uint16_t *add_move(uint16_t * moves, bool promotes, unsigned from,
 
    unsigned to) {
 
  if (!promotes)
 
    *moves++ = make_move(TB_PROMOTES_NONE, from, to);
 
  else {
 
    *moves++ = make_move(TB_PROMOTES_QUEEN, from, to);
 
    *moves++ = make_move(TB_PROMOTES_KNIGHT, from, to);
 
    *moves++ = make_move(TB_PROMOTES_ROOK, from, to);
 
    *moves++ = make_move(TB_PROMOTES_BISHOP, from, to);
 
  }
 
  return moves;
 
}
 
 
 
/*
 
 * Generate all captures or promotions.
 
 */
 
static uint16_t *gen_captures_or_promotions(const struct pos *pos,
 
    uint16_t * moves) {
 
  uint64_t occ = pos->white | pos->black;
 
  uint64_t us = (pos->turn ? pos->white : pos->black), them =
 
      (pos->turn ? pos->black : pos->white);
 
  uint64_t b, att;
 
  {
 
    unsigned from = _lsb(pos->kings & us);
 
    for (att = king_attacks(from) & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->queens; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = queen_attacks(from, occ) & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->rooks; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = rook_attacks(from, occ) & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->bishops; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = bishop_attacks(from, occ) & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->knights; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = knight_attacks(from) & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->pawns; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    att = pawn_attacks(from, pos->turn);
 
    if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) {
 
      unsigned to = pos->ep;
 
      moves = add_move(moves, false, from, to);
 
    }
 
    for (att = att & them; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to);
 
    }
 
    if (pos->turn && rank(from) == 6) {
 
      unsigned to = from + 8;
 
      if ((board(to) & occ) == 0)
 
        moves = add_move(moves, true, from, to);
 
    } else if (!pos->turn && rank(from) == 1) {
 
      unsigned to = from - 8;
 
      if ((board(to) & occ) == 0)
 
        moves = add_move(moves, true, from, to);
 
    }
 
  }
 
  return moves;
 
}
 
 
 
/*
 
 * Generate all non-capture pawn moves and promotions.
 
 */
 
static uint16_t *gen_pawn_quiets_or_promotions(const struct pos *pos,
 
    uint16_t * moves) {
 
  uint64_t occ = pos->white | pos->black;
 
  uint64_t us = (pos->turn ? pos->white : pos->black);
 
  uint64_t b, att;
 
 
 
  for (b = us & pos->pawns; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    unsigned next = from + (pos->turn ? 8 : -8);
 
    att = 0;
 
    if ((board(next) & occ) == 0) {
 
      att |= board(next);
 
      unsigned next2 = from + (pos->turn ? 16 : -16);
 
      if ((pos->turn ? rank(from) == 1 : rank(from) == 6) &&
 
          ((board(next2) & occ) == 0))
 
        att |= board(next2);
 
    }
 
    for (; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to);
 
    }
 
  }
 
  return moves;
 
}
 
 
 
/*
 
 * Generate all en passant captures.
 
 */
 
static uint16_t *gen_pawn_ep_captures(const struct pos *pos, uint16_t * moves) {
 
  if (pos->ep == 0)
 
    return moves;
 
  uint64_t ep = board(pos->ep);
 
  unsigned to = pos->ep;
 
  uint64_t us = (pos->turn ? pos->white : pos->black);
 
  uint64_t b;
 
  for (b = us & pos->pawns; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    if ((pawn_attacks(from, pos->turn) & ep) != 0)
 
      moves = add_move(moves, false, from, to);
 
  }
 
  return moves;
 
}
 
 
 
/*
 
 * Generate all moves.
 
 */
 
static uint16_t *gen_moves(const struct pos *pos, uint16_t * moves) {
 
  uint64_t occ = pos->white | pos->black;
 
  uint64_t us = (pos->turn ? pos->white : pos->black), them =
 
      (pos->turn ? pos->black : pos->white);
 
  uint64_t b, att;
 
 
 
  {
 
    unsigned from = _lsb(pos->kings & us);
 
    for (att = king_attacks(from) & ~us; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->queens; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = queen_attacks(from, occ) & ~us; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->rooks; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = rook_attacks(from, occ) & ~us; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->bishops; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = bishop_attacks(from, occ) & ~us; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->knights; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    for (att = knight_attacks(from) & ~us; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, false, from, to);
 
    }
 
  }
 
  for (b = us & pos->pawns; b; b = poplsb(b)) {
 
    unsigned from = _lsb(b);
 
    unsigned next = from + (pos->turn ? 8 : -8);
 
    att = pawn_attacks(from, pos->turn);
 
    if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) {
 
      unsigned to = pos->ep;
 
      moves = add_move(moves, false, from, to);
 
    }
 
    att &= them;
 
    if ((board(next) & occ) == 0) {
 
      att |= board(next);
 
      unsigned next2 = from + (pos->turn ? 16 : -16);
 
      if ((pos->turn ? rank(from) == 1 : rank(from) == 6) &&
 
          ((board(next2) & occ) == 0))
 
        att |= board(next2);
 
    }
 
    for (; att; att = poplsb(att)) {
 
      unsigned to = _lsb(att);
 
      moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to);
 
    }
 
  }
 
  return moves;
 
}
 
 
 
/*
 
 * Test if the given move is an en passant capture.
 
 */
 
static bool is_en_passant(const struct pos *pos, uint16_t move) {
 
  uint16_t from = move_from(move);
 
  uint16_t to = move_to(move);
 
  uint64_t us = (pos->turn ? pos->white : pos->black);
 
  if (pos->ep == 0)
 
    return false;
 
  if (to != pos->ep)
 
    return false;
 
  if ((board(from) & us & pos->pawns) != 0)
 
    return false;
 
  return true;
 
}
 
 
 
/*
 
 * Test if the given position is legal (can the king be captured?)
 
 */
 
static bool is_legal(const struct pos *pos) {
 
  uint64_t occ = pos->white | pos->black;
 
  uint64_t us = (pos->turn ? pos->black : pos->white), them =
 
      (pos->turn ? pos->white : pos->black);
 
  uint64_t king = pos->kings & us;
 
  unsigned sq = _lsb(king);
 
  if (king_attacks(sq) & (pos->kings & them))
 
    return false;
 
  uint64_t ratt = rook_attacks(sq, occ);
 
  uint64_t batt = bishop_attacks(sq, occ);
 
  if (ratt & (pos->rooks & them))
 
    return false;
 
  if (batt & (pos->bishops & them))
 
    return false;
 
  if ((ratt | batt) & (pos->queens & them))
 
    return false;
 
  if (knight_attacks(sq) & (pos->knights & them))
 
    return false;
 
  if (pawn_attacks(sq, !pos->turn) & (pos->pawns & them))
 
    return false;
 
  return true;
 
}
 
 
 
/*
 
 * Test if the king is in check.
 
 */
 
static bool is_check(const struct pos *pos) {
 
  uint64_t occ = pos->white | pos->black;
 
  uint64_t us = (pos->turn ? pos->white : pos->black), them =
 
      (pos->turn ? pos->black : pos->white);
 
  uint64_t king = pos->kings & us;
 
  unsigned sq = _lsb(king);
 
  uint64_t ratt = rook_attacks(sq, occ);
 
  uint64_t batt = bishop_attacks(sq, occ);
 
  if (ratt & (pos->rooks & them))
 
    return true;
 
  if (batt & (pos->bishops & them))
 
    return true;
 
  if ((ratt | batt) & (pos->queens & them))
 
    return true;
 
  if (knight_attacks(sq) & (pos->knights & them))
 
    return true;
 
  if (pawn_attacks(sq, pos->turn) & (pos->pawns & them))
 
    return true;
 
  return false;
 
}
 
 
 
/*
 
 * Test if the king is in checkmate.
 
 */
 
static bool is_mate(const struct pos *pos) {
 
  if (!is_check(pos))
 
    return false;
 
  uint16_t moves0[MAX_MOVES];
 
  uint16_t *moves = moves0;
 
  uint16_t *end = gen_moves(pos, moves);
 
  for (; moves < end; moves++) {
 
    struct pos pos1;
 
    if (do_move(&pos1, pos, *moves))
 
      return false;
 
  }
 
  return true;
 
}
 
 
 
/*
 
 * Test if the position is valid.
 
 */
 
static bool is_valid(const struct pos *pos) {
 
  if (popcount(pos->kings) != 2)
 
    return false;
 
  if (popcount(pos->kings & pos->white) != 1)
 
    return false;
 
  if (popcount(pos->kings & pos->black) != 1)
 
    return false;
 
  if ((pos->white & pos->black) != 0)
 
    return false;
 
  if ((pos->kings & pos->queens) != 0)
 
    return false;
 
  if ((pos->kings & pos->rooks) != 0)
 
    return false;
 
  if ((pos->kings & pos->bishops) != 0)
 
    return false;
 
  if ((pos->kings & pos->knights) != 0)
 
    return false;
 
  if ((pos->kings & pos->pawns) != 0)
 
    return false;
 
  if ((pos->queens & pos->rooks) != 0)
 
    return false;
 
  if ((pos->queens & pos->bishops) != 0)
 
    return false;
 
  if ((pos->queens & pos->knights) != 0)
 
    return false;
 
  if ((pos->queens & pos->pawns) != 0)
 
    return false;
 
  if ((pos->rooks & pos->bishops) != 0)
 
    return false;
 
  if ((pos->rooks & pos->knights) != 0)
 
    return false;
 
  if ((pos->rooks & pos->pawns) != 0)
 
    return false;
 
  if ((pos->bishops & pos->knights) != 0)
 
    return false;
 
  if ((pos->bishops & pos->pawns) != 0)
 
    return false;
 
  if ((pos->knights & pos->pawns) != 0)
 
    return false;
 
  if ((pos->white | pos->black) !=
 
      (pos->kings | pos->queens | pos->rooks | pos->
 
          bishops | pos->knights | pos->pawns))
 
    return false;
 
  return is_legal(pos);
 
}
 
 
 
#define do_bb_move(b, from, to)                                         \
 
    (((b) & (~board(to)) & (~board(from))) |                            \
 
        ((((b) >> (from)) & 0x1) << (to)))
 
 
 
static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move) {
 
  unsigned from = move_from(move);
 
  unsigned to = move_to(move);
 
  unsigned promotes = move_promotes(move);
 
  pos->turn = !pos0->turn;
 
  pos->white = do_bb_move(pos0->white, from, to);
 
  pos->black = do_bb_move(pos0->black, from, to);
 
  pos->kings = do_bb_move(pos0->kings, from, to);
 
  pos->queens = do_bb_move(pos0->queens, from, to);
 
  pos->rooks = do_bb_move(pos0->rooks, from, to);
 
  pos->bishops = do_bb_move(pos0->bishops, from, to);
 
  pos->knights = do_bb_move(pos0->knights, from, to);
 
  pos->pawns = do_bb_move(pos0->pawns, from, to);
 
  pos->ep = 0;
 
  if (promotes != TB_PROMOTES_NONE) {
 
    pos->pawns &= ~board(to); // Promotion
 
    switch (promotes) {
 
      case TB_PROMOTES_QUEEN:
 
        pos->queens |= board(to);
 
        break;
 
      case TB_PROMOTES_ROOK:
 
        pos->rooks |= board(to);
 
        break;
 
      case TB_PROMOTES_BISHOP:
 
        pos->bishops |= board(to);
 
        break;
 
      case TB_PROMOTES_KNIGHT:
 
        pos->knights |= board(to);
 
        break;
 
    }
 
    pos->rule50 = 0;
 
  } else if ((board(from) & pos0->pawns) != 0) {
 
    pos->rule50 = 0; // Pawn move
 
    if (rank(from) == 1 && rank(to) == 3 &&
 
        (pawn_attacks(from + 8, true) & pos0->pawns & pos0->black) != 0)
 
      pos->ep = from + 8;
 
    else if (rank(from) == 6 && rank(to) == 4 &&
 
        (pawn_attacks(from - 8, false) & pos0->pawns & pos0->white) != 0)
 
      pos->ep = from - 8;
 
    else if (to == pos0->ep) {
 
      unsigned ep_to = (pos0->turn ? to + 8 : to - 8);
 
      uint64_t ep_mask = ~board(ep_to);
 
      pos->white &= ep_mask;
 
      pos->black &= ep_mask;
 
      pos->pawns &= ep_mask;
 
    }
 
  } else if ((board(to) & (pos0->white | pos0->black)) != 0)
 
    pos->rule50 = 0; // Capture
 
  else
 
    pos->rule50 = pos0->rule50 + 1; // Normal move
 
  if (!is_legal(pos))
 
    return false;
 
  return true;
 
}
 
 
 
static int probe_ab(const struct pos *pos, int alpha, int beta, int *success) {
 
  int v;
 
  uint16_t moves0[64];
 
  uint16_t *moves = moves0;
 
  uint16_t *end = gen_captures_or_promotions(pos, moves);
 
  for (; moves < end; moves++) {
 
    if (is_en_passant(pos, *moves))
 
      continue;
 
    struct pos pos1;
 
    if (!do_move(&pos1, pos, *moves))
 
      continue;
 
    v = -probe_ab(&pos1, -beta, -alpha, success);
 
    if (*success == 0)
 
      return 0;
 
    if (v > alpha) {
 
      if (v >= beta) {
 
        *success = 2;
 
        return v;
 
      }
 
      alpha = v;
 
    }
 
  }
 
 
 
  v = probe_wdl_table(pos, success);
 
  if (*success == 0)
 
    return 0;
 
  if (alpha >= v) {
 
    *success = 1 + (alpha > 0);
 
    return alpha;
 
  } else {
 
    *success = 1;
 
    return v;
 
  }
 
}
 
 
 
static int probe_wdl(const struct pos *pos, int *success) {
 
  *success = 1;
 
  int v = probe_ab(pos, -2, 2, success);
 
  if (*success == 0)
 
    return 0;
 
 
 
 // If en passant is not possible, we are done.
 
  if (pos->ep == 0)
 
    return v;
 
 
 
 // Now handle en passant.
 
  int v1 = -3;
 
  uint16_t moves0[2];           // Max=2 possible en-passant captures.
 
  uint16_t *moves = moves0;
 
  uint16_t *end = gen_pawn_ep_captures(pos, moves);
 
  for (; moves < end; moves++) {
 
    struct pos pos1;
 
    if (!do_move(&pos1, pos, *moves))
 
      continue;
 
    int v0 = -probe_ab(pos, -2, 2, success);
 
    if (*success == 0)
 
      return 0;
 
    if (v0 > v1)
 
      v1 = v0;
 
  }
 
  if (v1 > -3) {
 
    if (v1 >= v)
 
      v = v1;
 
    else if (v == 0) {
 
     // Check whether there is at least one legal non-ep move.
 
      uint16_t moves0[MAX_MOVES];
 
      uint16_t *moves = moves0;
 
      uint16_t *end = gen_moves(pos, moves);
 
      bool found = false;
 
      for (; moves < end; moves++) {
 
        if (is_en_passant(pos, *moves))
 
          continue;
 
        struct pos pos1;
 
        if (do_move(&pos1, pos, *moves)) {
 
          found = true;
 
          break;
 
        }
 
      }
 
      if (!found)
 
        v = v1; // Forced to play the losing ep capture.
 
    }
 
  }
 
 
 
  return v;
 
}
 
 
 
static int probe_dtz_no_ep(const struct pos *pos, int *success) {
 
  int wdl, dtz;
 
  wdl = probe_ab(pos, -2, 2, success);
 
  if (wdl == 0)
 
    return 0;
 
  if (*success == 2)
 
    return wdl == 2 ? 1 : 101;
 
 
 
  uint16_t moves0[MAX_MOVES];
 
  uint16_t *moves = moves0, *end = NULL;
 
 
 
  if (wdl > 0) {
 
   // Generate at least all legal non-capturing pawn moves
 
   // including non-capturing promotions.
 
    end = gen_pawn_quiets_or_promotions(pos, moves);
 
    for (; moves < end; moves++) {
 
      struct pos pos1;
 
      if (!do_move(&pos1, pos, *moves))
 
        continue;
 
     //int v = pos.ep_square() == SQ_NONE ? -probe_ab(pos, -2, -wdl + 1, success)
 
     //                                   : -probe_wdl(pos, success);
 
     //int v = -probe_ab(&pos1, -2, -wdl + 1, success);removed mfb
 
      int v = (pos1.ep == 0 ? -probe_ab(&pos1, -2, -wdl + 1, success) : -probe_wdl(&pos1, success)); //added mfb
 
      if (*success == 0)
 
        return 0;
 
      if (v == wdl)
 
        return (v == 2 ? 1 : 101);
 
    }
 
  }
 
 
 
  dtz = 1 + probe_dtz_table(pos, wdl, success);
 
  if (*success >= 0) {
 
    if (wdl & 1)
 
      dtz += 100;
 
    return (wdl >= 0 ? dtz : -dtz);
 
  }
 
 
 
  if (wdl > 0) {
 
    int best = BEST_NONE;
 
    moves = moves0;
 
    end = gen_moves(pos, moves);
 
    for (; moves < end; moves++) {
 
      struct pos pos1;
 
      if (!do_move(&pos1, pos, *moves))
 
        continue;
 
      if (pos1.rule50 == 0)
 
        continue;
 
      int v = -probe_dtz(&pos1, success);
 
      if (*success == 0)
 
        return 0;
 
      if (v > 0 && v + 1 < best)
 
        best = v + 1;
 
    }
 
    return best;
 
  } else {
 
    int best = -1;
 
    end = gen_moves(pos, moves);
 
    for (; moves < end; moves++) {
 
      int v;
 
      struct pos pos1;
 
      if (!do_move(&pos1, pos, *moves))
 
        continue;
 
      if (pos1.rule50 == 0) {
 
        if (wdl == -2)
 
          v = -1;
 
        else {
 
          v = probe_ab(&pos1, 1, 2, success);
 
          v = (v == 2) ? 0 : -101;
 
        }
 
      } else
 
        v = -probe_dtz(&pos1, success) - 1;
 
      if (*success == 0)
 
        return 0;
 
      if (v < best)
 
        best = v;
 
    }
 
    return best;
 
  }
 
}
 
 
 
static const int wdl_to_dtz[] = {
 
  -1, -101, 0, 101, 1
 
};
 
 
 
/*
 
 * Probe the DTZ table for a particular position.
 
 * If *success != 0, the probe was successful.
 
 * The return value is from the point of view of the side to move:
 
 *         n < -100 : loss, but draw under 50-move rule
 
 * -100 <= n < -1   : loss in n ply (assuming 50-move counter == 0)
 
 *         0            : draw
 
 *     1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
 
 *   100 < n        : win, but draw under 50-move rule
 
 *
 
 * The return value n can be off by 1: a return value -n can mean a loss
 
 * in n+1 ply and a return value +n can mean a win in n+1 ply. This
 
 * cannot happen for tables with positions exactly on the "edge" of
 
 * the 50-move rule.
 
 *
 
 * This implies that if dtz > 0 is returned, the position is certainly
 
 * a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
 
 * picks moves that preserve dtz + 50-move-counter <= 99.
 
 *
 
 * If n = 100 immediately after a capture or pawn move, then the position
 
 * is also certainly a win, and during the whole phase until the next
 
 * capture or pawn move, the inequality to be preserved is
 
 * dtz + 50-movecounter <= 100.
 
 *
 
 * In short, if a move is available resulting in dtz + 50-move-counter <= 99,
 
 * then do not accept moves leading to dtz + 50-move-counter == 100.
 
 */
 
static int probe_dtz(const struct pos *pos, int *success) {
 
  *success = 1;
 
  int v = probe_dtz_no_ep(pos, success);
 
  if (*success == 0)
 
    return 0;
 
 
 
  if (pos->ep == 0)
 
    return v;
 
 
 
  int v1 = -3;
 
  uint16_t moves0[2];           // Max=2 possible en-passant captures.
 
  uint16_t *moves = moves0;
 
  uint16_t *end = gen_pawn_ep_captures(pos, moves);
 
  for (; moves < end; moves++) {
 
    struct pos pos1;
 
    if (!do_move(&pos1, pos, *moves))
 
      continue;
 
    int v0 = -probe_ab(pos, -2, 2, success);
 
    if (*success == 0)
 
      return 0;
 
    if (v0 > v1)
 
      v1 = v0;
 
  }
 
 
 
  if (v1 > -3) {
 
    v1 = wdl_to_dtz[v1 + 2];
 
    if (v < -100) {
 
      if (v1 >= 0)
 
        v = v1;
 
    } else if (v < 0) {
 
      if (v1 >= 0 || v1 < -100)
 
        v = v1;
 
    } else if (v > 100) {
 
      if (v1 > 0)
 
        v = v1;
 
    } else if (v > 0) {
 
      if (v1 == 1)
 
        v = v1;
 
    } else if (v1 >= 0)
 
      v = v1;
 
    else {
 
      uint16_t moves0[MAX_MOVES];
 
      uint16_t *moves = moves0;
 
      uint16_t *end = gen_moves(pos, moves);
 
      bool found = false;
 
      for (; moves < end; moves++) {
 
        if (is_en_passant(pos, *moves))
 
          continue;
 
        struct pos pos1;
 
        if (do_move(&pos1, pos, *moves)) {
 
          found = true;
 
          break;
 
        }
 
      }
 
      if (!found)
 
        v = v1; // Forced to play the losing ep capture.
 
    }
 
  }
 
 
 
  return v;
 
}
 
 
 
unsigned dtz_to_wdl(int cnt50, int dtz) {
 
  int wdl = 0;
 
  if (dtz > 0)
 
    wdl = (dtz + cnt50 <= 100 ? 2 : 1);
 
  else if (dtz < 0)
 
    wdl = (-dtz + cnt50 <= 100 ? -2 : -1);
 
  return wdl + 2;
 
}
 
 
 
static uint16_t probe_root(const struct pos *pos, int *score,
 
    unsigned *results) {
 
  int success;
 
  int dtz = probe_dtz(pos, &success);
 
  if (!success)
 
    return 0;
 
 
 
  int16_t scores[MAX_MOVES];
 
  uint16_t moves0[MAX_MOVES];
 
  uint16_t *moves = moves0;
 
  uint16_t *end = gen_moves(pos, moves);
 
  size_t len = end - moves;
 
  size_t num_draw = 0;
 
  unsigned j = 0;
 
  unsigned i = 0;               //mfb
 
  for (i = 0; i < len; i++) {
 
    struct pos pos1;
 
    if (!do_move(&pos1, pos, moves[i])) {
 
      scores[i] = SCORE_ILLEGAL;
 
      continue;
 
    }
 
    int v = 0;
 
    if (dtz > 0 && is_mate(&pos1))
 
      v = 1;
 
    else {
 
      if (pos1.rule50 != 0) {
 
        v = -probe_dtz(&pos1, &success);
 
        if (v > 0)
 
          v++;
 
        else if (v < 0)
 
          v--;
 
      } else {
 
        v = -probe_wdl(&pos1, &success);
 
        v = wdl_to_dtz[v + 2];
 
      }
 
    }
 
    num_draw += (v == 0);
 
    if (!success)
 
      return 0;
 
    scores[i] = v;
 
    if (results != NULL) {
 
      unsigned res = 0;
 
      res = TB_SET_WDL(res, dtz_to_wdl(pos->rule50, v));
 
      res = TB_SET_FROM(res, move_from(moves[i]));
 
      res = TB_SET_TO(res, move_to(moves[i]));
 
      res = TB_SET_PROMOTES(res, move_promotes(moves[i]));
 
      res = TB_SET_EP(res, is_en_passant(pos, moves[i]));
 
      res = TB_SET_DTZ(res, (dtz < 0 ? -dtz : dtz));
 
      results[j++] = res;
 
    }
 
  }
 
  if (results != NULL)
 
    results[j++] = TB_RESULT_FAILED;
 
  if (score != NULL)
 
    *score = dtz;
 
 
 
 // Now be a bit smart about filtering out moves.
 
  if (dtz > 0) // winning (or 50-move rule draw)
 
  {
 
    int best = BEST_NONE;
 
    uint16_t best_move = 0;
 
    for (i = 0; i < len; i++) {
 
      int v = scores[i];
 
      if (v == SCORE_ILLEGAL)
 
        continue;
 
      if (v > 0 && v < best) {
 
        best = v;
 
        best_move = moves[i];
 
      }
 
    }
 
    return (best == BEST_NONE ? 0 : best_move);
 
  } else if (dtz < 0) // losing (or 50-move rule draw)
 
  {
 
    int best = 0;
 
    uint16_t best_move = 0;
 
    for (i = 0; i < len; i++) {
 
      int v = scores[i];
 
      if (v == SCORE_ILLEGAL)
 
        continue;
 
      if (v < best) {
 
        best = v;
 
        best_move = moves[i];
 
      }
 
    }
 
    return (best == 0 ? MOVE_CHECKMATE : best_move);
 
  } else // drawing
 
  {
 
   // Check for stalemate:
 
    if (num_draw == 0)
 
      return MOVE_STALEMATE;
 
 
 
   // Select a "random" move that preserves the draw.
 
   // Uses calc_key as the PRNG.
 
    size_t count = calc_key(pos, !pos->turn) % num_draw;
 
    for (i = 0; i < len; i++) {
 
      int v = scores[i];
 
      if (v == SCORE_ILLEGAL)
 
        continue;
 
      if (v == 0) {
 
        if (count == 0)
 
          return moves[i];
 
        count--;
 
      }
 
    }
 
    return 0;
 
  }
 
}
 
 
 
extern bool tb_init_impl(const char *path) {
 
  if (sizeof(uint64_t) != 8 && // Paranoid check
 
      sizeof(uint32_t) != 4 && sizeof(uint16_t) != 2 && sizeof(uint8_t) != 1)
 
    return false;
 
  king_attacks_init();
 
  knight_attacks_init();
 
  bishop_attacks_init();
 
  rook_attacks_init();
 
  pawn_attacks_init();
 
  if (path == NULL)
 
    path = "";
 
  init_tablebases(path);
 
  return true;
 
}
 
 
 
extern unsigned tb_probe_wdl_impl(uint64_t white, uint64_t black,
 
    uint64_t kings, uint64_t queens, uint64_t rooks, uint64_t bishops,
 
    uint64_t knights, uint64_t pawns, unsigned ep, bool turn, uint64_t hash) {
 
  struct pos pos = {
 
    white,
 
    black,
 
    kings,
 
    queens,
 
    rooks,
 
    bishops,
 
    knights,
 
    pawns,
 
    0,
 
    ep,
 
    turn
 
  };
 
  static uint64_t cache[4096] = { 0 };
 
  uint64_t idx = hash % 1024;
 
  if ((cache[idx] & ~0x7) == (hash & ~0x7)) {
 
   // Hit
 
//        FILE *F = fopen("debug.txt", "a");
 
//        fprintf(F, "CACHE_HIT = %.16lX (result=%u)\n", cache[idx],
 
//            (unsigned)(cache[idx] & 0x7));
 
//        fclose(F);
 
    return (cache[idx] & 0x7);
 
  }
 
 // Miss
 
  int success;
 
  int v = probe_wdl(&pos, &success);
 
  if (success == 0)
 
    return TB_RESULT_FAILED;
 
  unsigned result = (unsigned) (v + 2);
 
  hash &= ~0x7;
 
  hash |= (uint64_t) result;
 
  cache[idx] = hash;
 
  return result;
 
}
 
 
 
extern unsigned tb_probe_root_impl(uint64_t white, uint64_t black,
 
    uint64_t kings, uint64_t queens, uint64_t rooks, uint64_t bishops,
 
    uint64_t knights, uint64_t pawns, unsigned rule50, unsigned ep, bool turn,
 
    unsigned *results) {
 
  struct pos pos = {
 
    white,
 
    black,
 
    kings,
 
    queens,
 
    rooks,
 
    bishops,
 
    knights,
 
    pawns,
 
    rule50,
 
    ep,
 
    turn
 
  };
 
  int dtz;
 
  if (!is_valid(&pos))
 
    return TB_RESULT_FAILED;
 
  uint16_t move = probe_root(&pos, &dtz, results);
 
  if (move == 0)
 
    return TB_RESULT_FAILED;
 
  if (move == MOVE_CHECKMATE)
 
    return TB_RESULT_CHECKMATE;
 
  if (move == MOVE_STALEMATE)
 
    return TB_RESULT_STALEMATE;
 
  unsigned res = 0;
 
  res = TB_SET_WDL(res, dtz_to_wdl(rule50, dtz));
 
  res = TB_SET_FROM(res, move_from(move));
 
  res = TB_SET_TO(res, move_to(move));
 
  res = TB_SET_PROMOTES(res, move_promotes(move));
 
  res = TB_SET_EP(res, is_en_passant(&pos, move));
 
  return res;
 
}
 
 
 
#ifndef TB_NO_HELPER_API
 
 
 
unsigned tb_pop_count(uint64_t bb) {
 
  return popcount(bb);
 
}
 
 
 
unsigned tb_lsb(uint64_t bb) {
 
  return _lsb(bb);
 
}
 
 
 
uint64_t tb_pop_lsb(uint64_t bb) {
 
  return poplsb(bb);
 
}
 
 
 
uint64_t tb_king_attacks(unsigned sq) {
 
  return king_attacks(sq);
 
}
 
 
 
uint64_t tb_queen_attacks(unsigned sq, uint64_t occ) {
 
  return queen_attacks(sq, occ);
 
}
 
 
 
uint64_t tb_rook_attacks(unsigned sq, uint64_t occ) {
 
  return rook_attacks(sq, occ);
 
}
 
 
 
uint64_t tb_bishop_attacks(unsigned sq, uint64_t occ) {
 
  return bishop_attacks(sq, occ);
 
}
 
 
 
uint64_t tb_knight_attacks(unsigned sq) {
 
  return knight_attacks(sq);
 
}
 
 
 
uint64_t tb_pawn_attacks(unsigned sq, bool color) {
 
  return pawn_attacks(sq, color);
 
}
 
 
 
#endif                          /* TB_NO_HELPER_API */