/*
 
    Senpai 1.0 Copyright (C) 2014 Fabien Letouzey.
 
 
 
    This program is free software: you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation, either version 3 of the License, or
 
    (at your option) any later version.
 
 
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 
 
 
    You should have received a copy of the GNU General Public License
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 
 
 
// macros
 
 
 
#ifndef DEBUG
 
# ifndef NDEBUG // Pierre-Marie Baty -- protect macro redefinition
 
#  define NDEBUG
 
# endif // Pierre-Marie Baty -- protect macro redefinition
 
#endif
 
 
 
// C includes
 
 
 
#include <cassert> // needs NDEBUG
 
#include <cctype>
 
#include <cmath>
 
#include <cstdlib>
 
#include <ctime>
 
#ifdef _MSC_VER
 
#include <algorithm> // Pierre-Marie Baty -- for std::min and std::max
 
#include <windows.h> // Pierre-Marie Baty -- for ExitProcess()
 
#undef max // Pierre-Marie Baty -- for std::min and std::max
 
#undef min // Pierre-Marie Baty -- for std::min and std::max
 
#endif // _MSC_VER
 
 
 
// C++ includes
 
 
 
#include <fstream>
 
#include <iostream>
 
#include <sstream>
 
#include <string>
 
#include <vector>
 
 
 
// C++11 includes
 
 
 
#include <chrono>
 
#include <condition_variable>
 
#include <mutex>
 
#include <thread>
 
 
 
// macros
 
 
 
#ifdef _MSC_VER
 
#  define I64(n) (n##i64)
 
#  define U64(u) (u##ui64)
 
#else
 
#  define I64(n) (n##LL)
 
#  define U64(u) (u##ULL)
 
#endif
 
 
 
#ifndef __builtin_popcountll // Pierre-Marie Baty -- __builtin_popcountll support for MSVC 32-bit
 
int __builtin_popcountll (unsigned long long x) {
 
   static const unsigned long long m1 = 0x5555555555555555; //binary: 0101...
 
   static const unsigned long long m2 = 0x3333333333333333; //binary: 00110011..
 
   static const unsigned long long m4 = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...
 
   static const unsigned long long h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...
 
   x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
 
   x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits
 
   x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits
 
   return (int)((x * h01) >> 56);  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ...
 
}
 
#endif // Pierre-Marie Baty -- __builtin_popcountll support for MSVC 32-bit
 
 
 
#ifndef __builtin_ctzll // Pierre-Marie Baty -- __builtin_ctzll support for MSVC 32-bit
 
int __builtin_ctzll (unsigned long long x)
 
{
 
   // Returns the number of trailing 0-bits in x, starting at the least significant bit position.
 
   // If x is zero, the result is undefined.
 
   // This uses a binary search algorithm from Hacker's Delight.
 
   int n = 1;
 
   if ((x & 0x00000000FFFFFFFF) == 0) { n = n + 32; x = x >> 32; }
 
   if ((x & 0x000000000000FFFF) == 0) { n = n + 16; x = x >> 16; }
 
   if ((x & 0x00000000000000FF) == 0) { n = n +  8; x = x >>  8; }
 
   if ((x & 0x000000000000000F) == 0) { n = n +  4; x = x >>  4; }
 
   if ((x & 0x0000000000000003) == 0) { n = n +  2; x = x >>  2; }
 
   return (n - (x & 1));
 
}
 
#endif // Pierre-Marie Baty -- __builtin_ctzll support for MSVC 32-bit
 
 
 
// types
 
 
 
typedef signed   char int8;
 
typedef unsigned char uint8;
 
 
 
typedef signed   short int int16;
 
typedef unsigned short int uint16;
 
 
 
typedef signed   int int32;
 
typedef unsigned int uint32;
 
 
 
typedef signed   long long int int64;
 
typedef unsigned long long int uint64;
 
 
 
typedef uint64 bit_t;
 
typedef uint64 hash_t; // key_t is used by Unix :(
 
 
 
// modules
 
 
 
namespace util {
 
 
 
class Timer {
 
 
 
private:
 
 
 
   typedef std::chrono::time_point<std::chrono::system_clock> time_t;
 
   typedef std::chrono::duration<int, std::ratio<1, 1000>> millisecond_t;
 
 
 
   int p_elapsed;
 
   bool p_running;
 
   time_t p_start;
 
 
 
   static time_t now() {
 
      return std::chrono::system_clock::now();
 
   }
 
 
 
   int time() const {
 
      assert(p_running);
 
      return std::chrono::duration_cast<millisecond_t>(now() - p_start).count();
 
   }
 
 
 
public:
 
 
 
   Timer() {
 
      reset();
 
   }
 
 
 
   void reset() {
 
      p_elapsed = 0;
 
      p_running = false;
 
   }
 
 
 
   void start() {
 
      p_start = now();
 
      p_running = true;
 
   }
 
 
 
   void stop() {
 
      p_elapsed += time();
 
      p_running = false;
 
   }
 
 
 
   int elapsed() const {
 
      int time = p_elapsed;
 
      if (p_running) time += this->time();
 
      return time;
 
   }
 
 
 
};
 
 
 
class Lockable {
 
 
 
protected: // HACK for Waitable::wait()
 
 
 
   mutable std::mutex p_mutex;
 
 
 
public:
 
 
 
   void lock   () const { p_mutex.lock(); }
 
   void unlock () const { p_mutex.unlock(); }
 
};
 
 
 
class Waitable : public Lockable {
 
 
 
private:
 
 
 
   std::condition_variable_any p_cond;
 
 
 
public:
 
 
 
   void wait   () { p_cond.wait(p_mutex); } // HACK: direct access
 
   void signal () { p_cond.notify_one(); }
 
};
 
 
 
int round(double x) {
 
   return int(std::floor(x + 0.5));
 
}
 
 
 
int div(int a, int b) {
 
 
 
   assert(b > 0);
 
 
 
   int div = a / b;
 
   if (a < 0 && a != b * div) div--; // fix buggy C semantics
 
 
 
   return div;
 
}
 
 
 
int sqrt(int n) {
 
   return int(std::sqrt(double(n)));
 
}
 
 
 
bool is_square(int n) {
 
   int i = sqrt(n);
 
   return i * i == n;
 
}
 
 
 
double rand_float() {
 
   return double(std::rand()) / (double(RAND_MAX) + 1.0);
 
}
 
 
 
int rand_int(int n) {
 
   assert(n > 0);
 
   return int(rand_float() * double(n));
 
}
 
 
 
int string_find(const std::string & s, char c) {
 
   return int(s.find(c));
 
}
 
 
 
bool string_case_equal(const std::string & s0, const std::string & s1) {
 
 
 
   if (s0.size() != s1.size()) return false;
 
 
 
   for (int i = 0; i < int(s0.size()); i++) {
 
      if (std::tolower(s0[i]) != std::tolower(s1[i])) return false;
 
   }
 
 
 
   return true;
 
}
 
 
 
bool to_bool(const std::string & s) {
 
   if (string_case_equal(s, "true")) {
 
      return true;
 
   } else if (string_case_equal(s, "false")) {
 
      return false;
 
   } else {
 
      std::cerr << "not a boolean: \"" << s << "\"" << std::endl;
 
      std::exit(EXIT_FAILURE);
 
   }
 
}
 
 
 
int64 to_int(const std::string & s) {
 
   std::stringstream ss(s);
 
   int64 n;
 
   ss >> n;
 
   return n;
 
}
 
 
 
std::string to_string(int n) {
 
   std::stringstream ss;
 
   ss << n;
 
   return ss.str();
 
}
 
 
 
std::string to_string(double x) {
 
   std::stringstream ss;
 
   ss << x;
 
   return ss.str();
 
}
 
 
 
void log(const std::string & s) {
 
   std::ofstream log_file("log.txt", std::ios_base::app);
 
   log_file << s << std::endl;
 
}
 
 
 
}
 
 
 
namespace input {
 
 
 
class Input : public util::Waitable {
 
 
 
   bool volatile p_has_input;
 
   bool p_eof;
 
   std::string p_line;
 
 
 
public:
 
 
 
   Input() {
 
      p_has_input = false;
 
      p_eof = false;
 
   }
 
 
 
   bool has_input() const {
 
      return p_has_input;
 
   }
 
 
 
   bool get_line(std::string & line) {
 
 
 
      lock();
 
 
 
      while (!p_has_input) {
 
         wait();
 
      }
 
 
 
      bool line_ok = !p_eof;
 
      if (line_ok) line = p_line;
 
 
 
      p_has_input = false;
 
      signal();
 
 
 
      unlock();
 
 
 
      return line_ok;
 
   }
 
 
 
   void set_eof() {
 
 
 
      lock();
 
 
 
      while (p_has_input) {
 
         wait();
 
      }
 
 
 
      p_eof = true;
 
 
 
      p_has_input = true;
 
      signal();
 
 
 
      unlock();
 
   }
 
 
 
   void set_line(std::string & line) {
 
 
 
      lock();
 
 
 
      while (p_has_input) {
 
         wait();
 
      }
 
 
 
      p_line = line;
 
 
 
      p_has_input = true;
 
      signal();
 
 
 
      unlock();
 
   }
 
 
 
};
 
 
 
Input input;
 
std::thread thread;
 
 
 
void input_program(Input * input) {
 
 
 
   std::string line;
 
 
 
   while (std::getline(std::cin, line)) {
 
      input->set_line(line);
 
   }
 
 
 
   input->set_eof();
 
}
 
 
 
void init() {
 
   thread = std::thread(input_program, &input);
 
   thread.detach();
 
}
 
 
 
};
 
 
 
namespace side {
 
 
 
const int SIZE = 2;
 
 
 
enum {
 
   WHITE,
 
   BLACK,
 
};
 
 
 
int opposit(int sd) {
 
   return sd ^ 1;
 
}
 
 
 
}
 
 
 
namespace square {
 
 
 
const int FILE_SIZE = 8;
 
const int RANK_SIZE = 8;
 
const int SIZE = FILE_SIZE * RANK_SIZE;
 
 
 
enum {
 
   FILE_A,
 
   FILE_B,
 
   FILE_C,
 
   FILE_D,
 
   FILE_E,
 
   FILE_F,
 
   FILE_G,
 
   FILE_H,
 
};
 
 
 
enum {
 
   RANK_1,
 
   RANK_2,
 
   RANK_3,
 
   RANK_4,
 
   RANK_5,
 
   RANK_6,
 
   RANK_7,
 
   RANK_8,
 
};
 
 
 
enum {
 
   NONE = -1,
 
   A1, A2, A3, A4, A5, A6, A7, A8,
 
   B1, B2, B3, B4, B5, B6, B7, B8,
 
   C1, C2, C3, C4, C5, C6, C7, C8,
 
   D1, D2, D3, D4, D5, D6, D7, D8,
 
   E1, E2, E3, E4, E5, E6, E7, E8,
 
   F1, F2, F3, F4, F5, F6, F7, F8,
 
   G1, G2, G3, G4, G5, G6, G7, G8,
 
   H1, H2, H3, H4, H5, H6, H7, H8,
 
};
 
 
 
enum {
 
   INC_LEFT  = -8,
 
   INC_RIGHT = +8,
 
};
 
 
 
const int CASTLING_DELTA = 16;
 
const int DOUBLE_PAWN_DELTA = 2;
 
 
 
int make(int fl, int rk) {
 
 
 
   assert(fl < 8);
 
   assert(rk < 8);
 
 
 
   return (fl << 3) | rk;
 
}
 
 
 
int make(int fl, int rk, int sd) {
 
 
 
   assert(fl < 8);
 
   assert(rk < 8);
 
 
 
   return make(fl, (rk ^ -sd) & 7);
 
}
 
 
 
int file(int sq) {
 
   return sq >> 3;
 
}
 
 
 
int rank(int sq) {
 
   return sq & 7;
 
}
 
 
 
int rank(int sq, int sd) {
 
   return (sq ^ -sd) & 7;
 
}
 
 
 
int opposit_file(int sq) {
 
   return sq ^ 070;
 
}
 
 
 
int opposit_rank(int sq) {
 
   return sq ^ 007;
 
}
 
 
 
bool is_promotion(int sq) {
 
   int rk = rank(sq);
 
   return rk == RANK_1 || rk == RANK_8;
 
}
 
 
 
int colour(int sq) {
 
   return ((sq >> 3) ^ sq) & 1;
 
}
 
 
 
bool same_colour(int s0, int s1) {
 
   int diff = s0 ^ s1;
 
   return (((diff >> 3) ^ diff) & 1) == 0;
 
}
 
 
 
bool same_line(int s0, int s1) {
 
   return file(s0) == file(s1) || rank(s0) == rank(s1);
 
}
 
 
 
int file_distance(int s0, int s1) {
 
   return std::abs(file(s1) - file(s0));
 
}
 
 
 
int rank_distance(int s0, int s1) {
 
   return std::abs(rank(s1) - rank(s0));
 
}
 
 
 
int distance(int s0, int s1) {
 
   return std::max(file_distance(s0, s1), rank_distance(s0, s1));
 
}
 
 
 
int pawn_inc(int sd) {
 
   return (sd == side::WHITE) ? +1 : -1;
 
}
 
 
 
int stop(int sq, int sd) {
 
   return sq + pawn_inc(sd);
 
}
 
 
 
int promotion(int sq, int sd) {
 
   return make(file(sq), RANK_8, sd);
 
}
 
 
 
bool is_valid_88(int s88) {
 
   return (s88 & ~0x77) == 0;
 
}
 
 
 
int to_88(int sq) {
 
   return sq + (sq & 070);
 
}
 
 
 
int from_88(int s88) {
 
   assert(is_valid_88(s88));
 
   return (s88 + (s88 & 7)) >> 1;
 
}
 
 
 
int from_fen(int sq) {
 
   return make(sq & 7, (sq >> 3) ^ 7);
 
}
 
 
 
int from_string(const std::string & s) {
 
   assert(s.length() == 2);
 
   return make(s[0] - 'a', s[1] - '1');
 
}
 
 
 
std::string to_string(int sq) {
 
 
 
   std::string s;
 
   s += 'a' + file(sq);
 
   s += '1' + rank(sq);
 
 
 
   return s;
 
}
 
 
 
}
 
 
 
namespace wing {
 
 
 
const int SIZE = 2;
 
 
 
enum {
 
   KING,
 
   QUEEN,
 
};
 
 
 
const int shelter_file[SIZE] = { square::FILE_G, square::FILE_B }; // for pawn-shelter eval
 
 
 
}
 
 
 
namespace piece {
 
 
 
const int SIZE = 7;
 
const int SIDE_SIZE = 12;
 
 
 
enum Piece {
 
   PAWN,
 
   KNIGHT,
 
   BISHOP,
 
   ROOK,
 
   QUEEN,
 
   KING,
 
   NONE,
 
};
 
 
 
enum Side_Piece {
 
   WHITE_PAWN,
 
   BLACK_PAWN,
 
   WHITE_KNIGHT,
 
   BLACK_KNIGHT,
 
   WHITE_BISHOP,
 
   BLACK_BISHOP,
 
   WHITE_ROOK,
 
   BLACK_ROOK,
 
   WHITE_QUEEN,
 
   BLACK_QUEEN,
 
   WHITE_KING,
 
   BLACK_KING,
 
};
 
 
 
const int PAWN_VALUE   = 100;
 
const int KNIGHT_VALUE = 325;
 
const int BISHOP_VALUE = 325;
 
const int ROOK_VALUE   = 500;
 
const int QUEEN_VALUE  = 975;
 
const int KING_VALUE   = 10000; // for SEE
 
 
 
const std::string Char = "PNBRQK?";
 
const std::string Fen_Char = "PpNnBbRrQqKk";
 
 
 
bool is_minor(int pc) {
 
   assert(pc < SIZE);
 
   return pc == KNIGHT || pc == BISHOP;
 
}
 
 
 
bool is_major(int pc) {
 
   assert(pc < SIZE);
 
   return pc == ROOK || pc == QUEEN;
 
}
 
 
 
bool is_slider(int pc) {
 
   assert(pc < SIZE);
 
   return pc >= BISHOP && pc <= QUEEN;
 
}
 
 
 
int score(int pc) { // for MVV/LVA
 
   assert(pc < SIZE);
 
   assert(pc != NONE);
 
   return pc;
 
}
 
 
 
int value(int pc) {
 
   assert(pc < SIZE);
 
   static const int value[SIZE] = { PAWN_VALUE, KNIGHT_VALUE, BISHOP_VALUE, ROOK_VALUE, QUEEN_VALUE, KING_VALUE, 0 };
 
   return value[pc];
 
}
 
 
 
int make(int pc, int sd) {
 
   assert(pc < SIZE);
 
   assert(pc != NONE);
 
   assert(sd < side::SIZE);
 
   return (pc << 1) | sd;
 
}
 
 
 
int piece(int p12) {
 
   assert(p12 < SIDE_SIZE);
 
   return p12 >> 1;
 
}
 
 
 
int side(int p12) {
 
   assert(p12 < SIDE_SIZE);
 
   return p12 & 1;
 
}
 
 
 
int from_char(char c) {
 
   return util::string_find(Char, c);
 
}
 
 
 
char to_char(int pc) {
 
   assert(pc < SIZE);
 
   return Char[pc];
 
}
 
 
 
int from_fen(char c) {
 
   return util::string_find(Fen_Char, c);
 
}
 
 
 
char to_fen(int p12) {
 
   assert(p12 < SIDE_SIZE);
 
   return Fen_Char[p12];
 
}
 
 
 
}
 
 
 
namespace move {
 
 
 
const int FLAGS_BITS = 9;
 
const int FLAGS_SIZE = 1 << FLAGS_BITS;
 
const int FLAGS_MASK = FLAGS_SIZE - 1;
 
 
 
const int BITS = FLAGS_BITS + 12;
 
const int SIZE = 1 << BITS;
 
const int MASK = SIZE - 1;
 
 
 
const int SCORE_BITS = 32 - BITS;
 
const int SCORE_SIZE = 1 << SCORE_BITS;
 
const int SCORE_MASK = SCORE_SIZE - 1;
 
 
 
enum Move {
 
   NONE  = 0,
 
   NULL_ = 1,
 
};
 
 
 
int make_flags(int pc, int cp, int pp = piece::NONE) {
 
 
 
   assert(pc < piece::SIZE);
 
   assert(cp < piece::SIZE);
 
   assert(pp < piece::SIZE);
 
 
 
   return (pc << 6) | (cp << 3) | pp;
 
}
 
 
 
int make(int f, int t, int pc, int cp, int pp = piece::NONE) {
 
 
 
   assert(f < square::SIZE);
 
   assert(t < square::SIZE);
 
   assert(pc < piece::SIZE);
 
   assert(cp < piece::SIZE);
 
   assert(pp < piece::SIZE);
 
 
 
   assert(pc != piece::NONE);
 
   assert(pp == piece::NONE || pc == piece::PAWN);
 
 
 
   return (pc << 18) | (cp << 15) | (pp << 12) | (f << 6) | t;
 
}
 
 
 
int from(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return (mv >> 6) & 077;
 
}
 
 
 
int to(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return mv & 077;
 
}
 
 
 
int piece(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return (mv >> 18) & 7;
 
}
 
 
 
int cap(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return (mv >> 15) & 7;
 
}
 
 
 
int prom(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return (mv >> 12) & 7;
 
}
 
 
 
int flags(int mv) {
 
   assert(mv != NONE);
 
   assert(mv != NULL_);
 
   return (mv >> 12) & 0777;
 
}
 
 
 
std::string to_can(int mv) {
 
 
 
   assert(mv != NONE);
 
 
 
   if (mv == NONE)  return "0000";
 
   if (mv == NULL_) return "0000";
 
 
 
   std::string s;
 
 
 
   s += square::to_string(from(mv));
 
   s += square::to_string(to(mv));
 
 
 
   if (prom(mv) != piece::NONE) {
 
      s += std::tolower(piece::to_char(prom(mv)));
 
   }
 
 
 
   return s;
 
}
 
 
 
}
 
 
 
namespace bit {
 
 
 
bit_t p_left[8];
 
bit_t p_right[8];
 
bit_t p_front[8];
 
bit_t p_rear[8];
 
 
 
bit_t p_side_front[side::SIZE][8];
 
bit_t p_side_rear[side::SIZE][8];
 
 
 
bit_t bit(int n) {
 
   assert(n < 64);
 
   return U64(1) << n;
 
}
 
 
 
void set(bit_t & b, int n) {
 
   b |= bit(n);
 
}
 
 
 
void clear(bit_t & b, int n) {
 
   b &= ~bit(n);
 
}
 
 
 
bool is_set(bit_t b, int n) {
 
   return (b & bit(n)) != 0;
 
}
 
 
 
int first(bit_t b) {
 
 
 
   assert(b != 0);
 
 
 
/*
 
   static const int index[64] = {
 
       0,  1,  2,  7,  3, 13,  8, 19,
 
       4, 25, 14, 28,  9, 34, 20, 40,
 
       5, 17, 26, 38, 15, 46, 29, 48,
 
      10, 31, 35, 54, 21, 50, 41, 57,
 
      63,  6, 12, 18, 24, 27, 33, 39,
 
      16, 37, 45, 47, 30, 53, 49, 56,
 
      62, 11, 23, 32, 36, 44, 52, 55,
 
      61, 22, 43, 51, 60, 42, 59, 58,
 
   };
 
 
 
   return index[((b & -b) * U64(0x218A392CD3D5DBF)) >> (64 - 6)];
 
*/
 
 
 
   return __builtin_ctzll(b); // GCC
 
}
 
 
 
bit_t rest(bit_t b) {
 
   assert(b != 0);
 
   return b & (b - 1);
 
}
 
 
 
int count(bit_t b) {
 
 
 
/*
 
   b = b - ((b >> 1) & U64(0x5555555555555555));
 
   b = (b & U64(0x3333333333333333)) + ((b >> 2) & U64(0x3333333333333333));
 
   b = (b + (b >> 4)) & U64(0x0F0F0F0F0F0F0F0F);
 
   return (b * U64(0x0101010101010101)) >> 56;
 
*/
 
 
 
   return __builtin_popcountll(b); // GCC
 
}
 
 
 
int count_loop(bit_t b) {
 
 
 
/*
 
   int n = 0;
 
 
 
   for (; b != 0; b = rest(b)) {
 
      n++;
 
   }
 
 
 
   return n;
 
*/
 
 
 
   return __builtin_popcountll(b); // GCC
 
}
 
 
 
bool single(bit_t b) {
 
   assert(b != 0);
 
   return rest(b) == 0;
 
}
 
 
 
bit_t file(int fl) {
 
   assert(fl < 8);
 
   return U64(0xFF) << (fl * 8);
 
}
 
 
 
bit_t rank(int rk) {
 
   assert(rk < 8);
 
   return U64(0x0101010101010101) << rk;
 
}
 
 
 
bit_t files(int fl) {
 
   assert(fl < 8);
 
   bit_t file = bit::file(fl);
 
   return (file << 8) | file | (file >> 8);
 
}
 
 
 
bit_t left(int fl) {
 
   assert(fl < 8);
 
   return p_left[fl];
 
}
 
 
 
bit_t right(int fl) {
 
   assert(fl < 8);
 
   return p_right[fl];
 
}
 
 
 
bit_t front(int rk) {
 
   assert(rk < 8);
 
   return p_front[rk];
 
}
 
 
 
bit_t rear(int rk) {
 
   assert(rk < 8);
 
   return p_rear[rk];
 
}
 
 
 
bit_t front(int sq, int sd) {
 
   int rk = square::rank(sq);
 
   return p_side_front[sd][rk];
 
}
 
 
 
bit_t rear(int sq, int sd) {
 
   int rk = square::rank(sq);
 
   return p_side_rear[sd][rk];
 
}
 
 
 
void init() {
 
 
 
   {
 
      bit_t bf = 0;
 
      bit_t br = 0;
 
 
 
      for (int i = 0; i < 8; i++) {
 
         p_left[i] = bf;
 
         p_rear[i] = br;
 
         bf |= file(i);
 
         br |= rank(i);
 
      }
 
   }
 
 
 
   {
 
      bit_t bf = 0;
 
      bit_t br = 0;
 
 
 
      for (int i = 7; i >= 0; i--) {
 
         p_right[i] = bf;
 
         p_front[i] = br;
 
         bf |= file(i);
 
         br |= rank(i);
 
      }
 
   }
 
 
 
   for (int rk = 0; rk < 8; rk++) {
 
      p_side_front[side::WHITE][rk] = front(rk);
 
      p_side_front[side::BLACK][rk] = rear(rk);
 
      p_side_rear [side::WHITE][rk] = rear(rk);
 
      p_side_rear [side::BLACK][rk] = front(rk);
 
   }
 
}
 
 
 
}
 
 
 
namespace hash {
 
 
 
const int TURN       = piece::SIDE_SIZE * square::SIZE;
 
const int CASTLE     = TURN + 1;
 
const int EN_PASSANT = CASTLE + 4;
 
const int SIZE       = EN_PASSANT + 8;
 
 
 
hash_t p_rand[SIZE];
 
 
 
hash_t rand_64() {
 
 
 
   hash_t rand = 0;
 
 
 
   for (int i = 0; i < 4; i++) {
 
      rand = (rand << 16) | util::rand_int(1 << 16);
 
   }
 
 
 
   return rand;
 
}
 
 
 
hash_t rand_key(int index) {
 
   assert(index >= 0 && index < SIZE);
 
   return p_rand[index];
 
}
 
 
 
hash_t piece_key(int p12, int sq) {
 
   return rand_key(p12 * square::SIZE + sq);
 
}
 
 
 
hash_t turn_key(int turn) {
 
   return (turn == side::WHITE) ? 0 : rand_key(TURN);
 
}
 
 
 
hash_t turn_flip() {
 
   return rand_key(TURN);
 
}
 
 
 
hash_t flag_key(int flag) {
 
   assert(flag < 4);
 
   return rand_key(CASTLE + flag);
 
}
 
 
 
hash_t en_passant_key(int sq) {
 
   return (sq == square::NONE) ? 0 : rand_key(EN_PASSANT + square::file(sq));
 
}
 
 
 
int64 index(hash_t key) {
 
   return int64(key);
 
}
 
 
 
uint32 lock(hash_t key) {
 
   return uint32(key >> 32);
 
}
 
 
 
void init() {
 
   for (int i = 0; i < SIZE; i++) {
 
      p_rand[i] = rand_64();
 
   }
 
}
 
 
 
}
 
 
 
namespace castling {
 
 
 
struct Info {
 
   int kf;
 
   int kt;
 
   int rf;
 
   int rt;
 
};
 
 
 
const Info info[4] = {
 
   { square::E1, square::G1, square::H1, square::F1 },
 
   { square::E1, square::C1, square::A1, square::D1 },
 
   { square::E8, square::G8, square::H8, square::F8 },
 
   { square::E8, square::C8, square::A8, square::D8 },
 
};
 
 
 
int flags_mask[square::SIZE];
 
hash_t flags_key[1 << 4];
 
 
 
int index(int sd, int wg) {
 
   return sd * 2 + wg;
 
}
 
 
 
int side(int index) {
 
   return index / 2;
 
}
 
 
 
bool flag(int flags, int index) {
 
   assert(index < 4);
 
   return bool((flags >> index) & 1);
 
}
 
 
 
void set_flag(int & flags, int index) {
 
   assert(index < 4);
 
   flags |= 1 << index;
 
}
 
 
 
hash_t flags_key_debug(int flags) {
 
 
 
   hash_t key = 0;
 
 
 
   for (int index = 0; index < 4; index++) {
 
      if (flag(flags, index)) {
 
         key ^= hash::flag_key(index);
 
      }
 
   }
 
 
 
   return key;
 
}
 
 
 
void init() {
 
 
 
   for (int sq = 0; sq < square::SIZE; sq++) {
 
      flags_mask[sq] = 0;
 
   }
 
 
 
   set_flag(flags_mask[square::E1], 0);
 
   set_flag(flags_mask[square::E1], 1);
 
   set_flag(flags_mask[square::H1], 0);
 
   set_flag(flags_mask[square::A1], 1);
 
 
 
   set_flag(flags_mask[square::E8], 2);
 
   set_flag(flags_mask[square::E8], 3);
 
   set_flag(flags_mask[square::H8], 2);
 
   set_flag(flags_mask[square::A8], 3);
 
 
 
   for (int flags = 0; flags < (1 << 4); flags++) {
 
      flags_key[flags] = flags_key_debug(flags);
 
   }
 
}
 
 
 
}
 
 
 
namespace stage {
 
 
 
const int SIZE = 2;
 
 
 
enum {
 
   MG,
 
   EG,
 
};
 
 
 
}
 
 
 
namespace material {
 
 
 
const int PAWN_PHASE   = 0;
 
const int KNIGHT_PHASE = 1;
 
const int BISHOP_PHASE = 1;
 
const int ROOK_PHASE   = 2;
 
const int QUEEN_PHASE  = 4;
 
 
 
const int TOTAL_PHASE = PAWN_PHASE * 16 + KNIGHT_PHASE * 4 + BISHOP_PHASE * 4 + ROOK_PHASE * 4 + QUEEN_PHASE * 2;
 
 
 
const int p_phase[piece::SIZE] = { PAWN_PHASE, KNIGHT_PHASE, BISHOP_PHASE, ROOK_PHASE, QUEEN_PHASE, 0, 0 };
 
 
 
int phase(int pc) {
 
   assert(pc < piece::SIZE);
 
   return p_phase[pc];
 
}
 
 
 
}
 
 
 
namespace board {
 
 
 
struct Copy {
 
   hash_t key;
 
   hash_t pawn_key;
 
   int flags;
 
   int ep_sq;
 
   int moves;
 
   int recap;
 
   int phase;
 
};
 
 
 
struct Undo {
 
   Copy copy;
 
   int move;
 
   int cap_sq;
 
   bool castling;
 
};
 
 
 
const std::string start_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -";
 
 
 
class Board {
 
 
 
private:
 
 
 
   static const int SCORE_NONE = -10000; // HACK because "score::NONE" is defined later
 
 
 
   bit_t p_piece[piece::SIZE];
 
   bit_t p_side[side::SIZE];
 
   bit_t p_all;
 
 
 
   int p_king[side::SIZE];
 
   int p_count[piece::SIDE_SIZE];
 
 
 
   int p_square[square::SIZE];
 
   int p_turn;
 
   Copy p_copy;
 
 
 
   int p_root;
 
   int p_sp;
 
   Undo p_stack[1024];
 
 
 
public:
 
 
 
   void operator=(const Board & bd) {
 
 
 
      for (int pc = 0; pc < piece::SIZE; pc++) {
 
         p_piece[pc] = bd.p_piece[pc];
 
      }
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         p_side[sd] = bd.p_side[sd];
 
         p_king[sd] = bd.p_king[sd];
 
      }
 
 
 
      p_all = bd.p_all;
 
 
 
      for (int p12 = 0; p12 < piece::SIDE_SIZE; p12++) {
 
         p_count[p12] = bd.p_count[p12];
 
      }
 
 
 
      for (int sq = 0; sq < square::SIZE; sq++) {
 
         p_square[sq] = bd.p_square[sq];
 
      }
 
 
 
      p_turn = bd.p_turn;
 
      p_copy = bd.p_copy;
 
 
 
      p_root = bd.p_root;
 
      p_sp = bd.p_sp;
 
 
 
      for (int sp = 0; sp < bd.p_sp; sp++) {
 
         p_stack[sp] = bd.p_stack[sp];
 
      }
 
 
 
      assert(moves() == bd.moves());
 
   }
 
 
 
   bit_t piece(int pc) const {
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      return p_piece[pc];
 
   }
 
 
 
   bit_t piece(int pc, int sd) const {
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      return p_piece[pc] & p_side[sd];
 
   }
 
 
 
   int count(int pc, int sd) const {
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      // return bit::count(piece(pc, sd));
 
      return p_count[piece::make(pc, sd)];
 
   }
 
 
 
   bit_t side(int sd) const {
 
      return p_side[sd];
 
   }
 
 
 
   bit_t pieces(int sd) const {
 
      return p_side[sd] & ~piece(piece::PAWN, sd);
 
   }
 
 
 
   bit_t all() const {
 
      return p_all;
 
   }
 
 
 
   bit_t empty() const {
 
      return ~p_all;
 
   }
 
 
 
   int square(int sq) const {
 
      return p_square[sq];
 
   }
 
 
 
   int square_side(int sq) const {
 
      assert(p_square[sq] != piece::NONE);
 
      return (p_side[side::BLACK] >> sq) & 1; // HACK: uses Side internals
 
   }
 
 
 
   bool square_is(int sq, int pc, int sd) const {
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      return square(sq) == pc && square_side(sq) == sd;
 
   }
 
 
 
   int king(int sd) const {
 
      int sq = p_king[sd];
 
      assert(sq == bit::first(piece(piece::KING, sd)));
 
      return sq;
 
   }
 
 
 
   int turn() const {
 
      return p_turn;
 
   }
 
 
 
   hash_t key() const {
 
      hash_t key = p_copy.key;
 
      key ^= castling::flags_key[p_copy.flags];
 
      key ^= hash::en_passant_key(p_copy.ep_sq);
 
      return key;
 
   }
 
 
 
   hash_t pawn_key() const {
 
      return p_copy.pawn_key;
 
   }
 
 
 
   hash_t eval_key() const {
 
      hash_t key = p_copy.key;
 
      key ^= hash::turn_key(p_turn); // remove incremental STM
 
      key ^= castling::flags_key[p_copy.flags];
 
      return key;
 
   }
 
 
 
   int flags() const {
 
      return p_copy.flags;
 
   }
 
 
 
   int ep_sq() const {
 
      return p_copy.ep_sq;
 
   }
 
 
 
   int moves() const {
 
      return p_copy.moves;
 
   }
 
 
 
   int recap() const {
 
      return p_copy.recap;
 
   }
 
 
 
   int phase() const {
 
      return p_copy.phase;
 
   }
 
 
 
   int ply() const {
 
      assert(p_sp >= p_root);
 
      return p_sp - p_root;
 
   }
 
 
 
   int last_move() const {
 
      return (p_sp == 0) ? move::NONE : p_stack[p_sp - 1].move;
 
   }
 
 
 
   bool is_draw() const {
 
 
 
      if (p_copy.moves > 100) { // TODO: check for mate
 
         return true;
 
      }
 
 
 
      hash_t key = p_copy.key; // HACK: ignores castling flags and e.p. square
 
 
 
      assert(p_copy.moves <= p_sp);
 
 
 
      for (int i = 4; i < p_copy.moves; i += 2) {
 
         if (p_stack[p_sp - i].copy.key == key) {
 
            return true;
 
         }
 
      }
 
 
 
      return false;
 
   }
 
 
 
   void set_root() {
 
      p_root = p_sp;
 
   }
 
 
 
   void clear() {
 
 
 
      for (int pc = 0; pc < piece::SIZE; pc++) {
 
         p_piece[pc] = 0;
 
      }
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         p_side[sd] = 0;
 
      }
 
 
 
      for (int sq = 0; sq < square::SIZE; sq++) {
 
         p_square[sq] = piece::NONE;
 
      }
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         p_king[sd] = square::NONE;
 
      }
 
 
 
      for (int p12 = 0; p12 < piece::SIDE_SIZE; p12++) {
 
         p_count[p12] = 0;
 
      }
 
 
 
      p_turn = side::WHITE;
 
 
 
      p_copy.key = 0;
 
      p_copy.pawn_key = 0;
 
      p_copy.flags = 0;
 
      p_copy.ep_sq = square::NONE;
 
      p_copy.moves = 0;
 
      p_copy.recap = square::NONE;
 
      p_copy.phase = 0;
 
 
 
      p_root = 0;
 
      p_sp = 0;
 
   }
 
 
 
   void clear_square(int pc, int sd, int sq, bool update_copy) {
 
 
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      assert(sq >= 0 && sq < square::SIZE);
 
 
 
      assert(pc == p_square[sq]);
 
 
 
      assert(bit::is_set(p_piece[pc], sq));
 
      bit::clear(p_piece[pc], sq);
 
 
 
      assert(bit::is_set(p_side[sd], sq));
 
      bit::clear(p_side[sd], sq);
 
 
 
      assert(p_square[sq] != piece::NONE);
 
      p_square[sq] = piece::NONE;
 
 
 
      int p12 = piece::make(pc, sd);
 
 
 
      assert(p_count[p12] != 0);
 
      p_count[p12]--;
 
 
 
      if (update_copy) {
 
 
 
         hash_t key = hash::piece_key(p12, sq);
 
         p_copy.key ^= key;
 
         if (pc == piece::PAWN) p_copy.pawn_key ^= key;
 
 
 
         p_copy.phase -= material::phase(pc);
 
      }
 
   }
 
 
 
   void set_square(int pc, int sd, int sq, bool update_copy) {
 
 
 
      assert(pc < piece::SIZE);
 
      assert(pc != piece::NONE);
 
      assert(sq >= 0 && sq < square::SIZE);
 
 
 
      assert(!bit::is_set(p_piece[pc], sq));
 
      bit::set(p_piece[pc], sq);
 
 
 
      assert(!bit::is_set(p_side[sd], sq));
 
      bit::set(p_side[sd], sq);
 
 
 
      assert(p_square[sq] == piece::NONE);
 
      p_square[sq] = pc;
 
 
 
      if (pc == piece::KING) {
 
         p_king[sd] = sq;
 
      }
 
 
 
      int p12 = piece::make(pc, sd);
 
 
 
      p_count[p12]++;
 
 
 
      if (update_copy) {
 
 
 
         hash_t key = hash::piece_key(p12, sq);
 
         p_copy.key ^= key;
 
         if (pc == piece::PAWN) p_copy.pawn_key ^= key;
 
 
 
         p_copy.phase += material::phase(pc);
 
      }
 
   }
 
 
 
   void move_square(int pc, int sd, int f, int t, bool update_copy) { // TODO
 
      clear_square(pc, sd, f, update_copy);
 
      set_square(pc, sd, t, update_copy);
 
   }
 
 
 
   void flip_turn() {
 
      p_turn = side::opposit(p_turn);
 
      p_copy.key ^= hash::turn_flip();
 
   }
 
 
 
   void update() {
 
 
 
      p_all = p_side[side::WHITE] | p_side[side::BLACK];
 
 
 
#ifdef DEBUG
 
 
 
      for (int p12 = 0; p12 < piece::SIDE_SIZE; p12++) {
 
         assert(p_count[p12] == bit::count(piece(piece::piece(p12), piece::side(p12))));
 
      }
 
 
 
      assert(p_count[piece::WHITE_KING] == 1);
 
      assert(p_count[piece::BLACK_KING] == 1);
 
 
 
      assert((p_piece[piece::PAWN] & bit::rank(square::RANK_1)) == 0);
 
      assert((p_piece[piece::PAWN] & bit::rank(square::RANK_8)) == 0);
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         for (int wg = 0; wg < side::SIZE; wg++) {
 
            int index = castling::index(sd, wg);
 
            if (castling::flag(p_copy.flags, index)) {
 
               assert(square_is(castling::info[index].kf, piece::KING, sd));
 
               assert(square_is(castling::info[index].rf, piece::ROOK, sd));
 
            }
 
         }
 
      }
 
 
 
#endif
 
 
 
   }
 
 
 
   bool can_castle(int index) const {
 
 
 
      int sd = castling::side(index);
 
 
 
      return square_is(castling::info[index].kf, piece::KING, sd)
 
          && square_is(castling::info[index].rf, piece::ROOK, sd);
 
   }
 
 
 
   bool pawn_is_attacked(int sq, int sd) const {
 
 
 
      int fl = square::file(sq);
 
      sq -= square::pawn_inc(sd);
 
 
 
      return (fl != square::FILE_A && square_is(sq + square::INC_LEFT,  piece::PAWN, sd))
 
          || (fl != square::FILE_H && square_is(sq + square::INC_RIGHT, piece::PAWN, sd));
 
   }
 
 
 
   void init_fen(const std::string & s) {
 
 
 
      clear();
 
 
 
      int pos = 0;
 
 
 
      // piece placement
 
 
 
      int sq = 0;
 
 
 
      while (pos < int(s.size())) {
 
 
 
         assert(sq <= square::SIZE);
 
 
 
         char c = s[pos++];
 
 
 
         if (false) {
 
 
 
         } else if (c == ' ') {
 
 
 
            break;
 
 
 
         } else if (c == '/') {
 
 
 
            continue;
 
 
 
         } else if (std::isdigit(c)) {
 
 
 
            sq += c - '0';
 
 
 
         } else { // assume piece
 
 
 
            int p12 = piece::from_fen(c);
 
            int pc = piece::piece(p12);
 
            int sd = piece::side(p12);
 
            set_square(pc, sd, square::from_fen(sq), true);
 
            sq++;
 
         }
 
      }
 
 
 
      assert(sq == square::SIZE);
 
 
 
      // turn
 
 
 
      p_turn = side::WHITE;
 
 
 
      if (pos < int(s.size())) {
 
 
 
         p_turn = util::string_find("wb", s[pos++]);
 
 
 
         if (pos < int(s.size())) {
 
            assert(s[pos] == ' ');
 
            pos++;
 
         }
 
      }
 
 
 
      p_copy.key ^= hash::turn_key(p_turn);
 
 
 
      // castling flags
 
 
 
      p_copy.flags = 0;
 
 
 
      if (pos < int(s.size())) { // read from FEN
 
 
 
         while (pos < int(s.size())) {
 
 
 
            char c = s[pos++];
 
            if (c == ' ') break;
 
            if (c == '-') continue;
 
 
 
            int index = util::string_find("KQkq", c);
 
 
 
            if (can_castle(index)) {
 
               castling::set_flag(p_copy.flags, index);
 
            }
 
         }
 
 
 
      } else { // guess from position
 
 
 
         for (int index = 0; index < 4; index++) {
 
            if (can_castle(index)) {
 
               castling::set_flag(p_copy.flags, index);
 
            }
 
         }
 
      }
 
 
 
      // en-passant square
 
 
 
      p_copy.ep_sq = square::NONE;
 
 
 
      if (pos < int(s.size())) { // read from FEN
 
 
 
         std::string ep_string;
 
 
 
         while (pos < int(s.size())) {
 
 
 
            char c = s[pos++];
 
            if (c == ' ') break;
 
 
 
            ep_string += c;
 
         }
 
 
 
         if (ep_string != "-") {
 
 
 
            int sq = square::from_string(ep_string);
 
 
 
            if (pawn_is_attacked(sq, p_turn)) {
 
               p_copy.ep_sq = sq;
 
            }
 
         }
 
      }
 
 
 
      update();
 
   }
 
 
 
   void move(int mv) {
 
 
 
      assert(mv != move::NONE);
 
      assert(mv != move::NULL_);
 
 
 
      int sd = p_turn;
 
      int xd = side::opposit(sd);
 
 
 
      int f = move::from(mv);
 
      int t = move::to(mv);
 
 
 
      int pc = move::piece(mv);
 
      int cp = move::cap(mv);
 
      int pp = move::prom(mv);
 
 
 
      assert(p_square[f] == pc);
 
      assert(square_side(f) == sd);
 
 
 
      assert(p_sp < 1024);
 
      Undo & undo = p_stack[p_sp++];
 
 
 
      undo.copy = p_copy;
 
      undo.move = mv;
 
      undo.castling = false;
 
 
 
      p_copy.moves++;
 
      p_copy.recap = square::NONE;
 
 
 
      // capture
 
 
 
      assert(cp != piece::KING);
 
 
 
      if (pc == piece::PAWN && t == p_copy.ep_sq) {
 
 
 
         int cap_sq = t - square::pawn_inc(sd);
 
         assert(p_square[cap_sq] == cp);
 
         assert(cp == piece::PAWN);
 
 
 
         undo.cap_sq = cap_sq;
 
 
 
         clear_square(cp, xd, cap_sq, true);
 
 
 
      } else if (cp != piece::NONE) {
 
 
 
         assert(p_square[t] == cp);
 
         assert(square_side(t) == xd);
 
 
 
         undo.cap_sq = t;
 
 
 
         clear_square(cp, xd, t, true);
 
 
 
      } else {
 
 
 
         assert(p_square[t] == cp);
 
      }
 
 
 
      // promotion
 
 
 
      if (pp != piece::NONE) {
 
         assert(pc == piece::PAWN);
 
         clear_square(piece::PAWN, sd, f, true);
 
         set_square(pp, sd, t, true);
 
      } else {
 
         move_square(pc, sd, f, t, true);
 
      }
 
 
 
      // castling rook
 
 
 
      if (pc == piece::KING && std::abs(t - f) == square::CASTLING_DELTA) {
 
 
 
         undo.castling = true;
 
 
 
         int wg = (t > f) ? wing::KING : wing::QUEEN;
 
         int index = castling::index(sd, wg);
 
 
 
         assert(castling::flag(p_copy.flags, index));
 
 
 
         assert(f == castling::info[index].kf);
 
         assert(t == castling::info[index].kt);
 
 
 
         move_square(piece::ROOK, sd, castling::info[index].rf, castling::info[index].rt, true);
 
      }
 
 
 
      // turn
 
 
 
      flip_turn();
 
 
 
      // castling flags
 
 
 
      p_copy.flags &= ~castling::flags_mask[f];
 
      p_copy.flags &= ~castling::flags_mask[t];
 
 
 
      // en-passant square
 
 
 
      p_copy.ep_sq = square::NONE;
 
 
 
      if (pc == piece::PAWN && std::abs(t - f) == square::DOUBLE_PAWN_DELTA) {
 
         int sq = (f + t) / 2;
 
         if (pawn_is_attacked(sq, xd)) {
 
            p_copy.ep_sq = sq;
 
         }
 
      }
 
 
 
      // move counter
 
 
 
      if (cp != piece::NONE || pc == piece::PAWN) {
 
         p_copy.moves = 0; // conversion;
 
      }
 
 
 
      // recapture
 
 
 
      if (cp != piece::NONE || pp != piece::NONE) {
 
         p_copy.recap = t;
 
      }
 
 
 
      update();
 
   }
 
 
 
   void undo() {
 
 
 
      assert(p_sp > 0);
 
      const Undo & undo = p_stack[--p_sp];
 
 
 
      int mv = undo.move;
 
 
 
      int f = move::from(mv);
 
      int t = move::to(mv);
 
 
 
      int pc = move::piece(mv);
 
      int cp = move::cap(mv);
 
      int pp = move::prom(mv);
 
 
 
      int xd = p_turn;
 
      int sd = side::opposit(xd);
 
 
 
      assert(p_square[t] == pc || p_square[t] == pp);
 
      assert(square_side(t) == sd);
 
 
 
      // castling rook
 
 
 
      if (undo.castling) {
 
 
 
         int wg = (t > f) ? wing::KING : wing::QUEEN;
 
         int index = castling::index(sd, wg);
 
 
 
         assert(f == castling::info[index].kf);
 
         assert(t == castling::info[index].kt);
 
 
 
         move_square(piece::ROOK, sd, castling::info[index].rt, castling::info[index].rf, false);
 
      }
 
 
 
      // promotion
 
 
 
      if (pp != piece::NONE) {
 
         assert(pc == piece::PAWN);
 
         clear_square(pp, sd, t, false);
 
         set_square(piece::PAWN, sd, f, false);
 
      } else {
 
         move_square(pc, sd, t, f, false);
 
      }
 
 
 
      // capture
 
 
 
      if (cp != piece::NONE) {
 
         set_square(cp, xd, undo.cap_sq, false);
 
      }
 
 
 
      flip_turn();
 
      p_copy = undo.copy;
 
 
 
      update();
 
   }
 
 
 
   void move_null() {
 
 
 
      assert(p_sp < 1024);
 
      Undo & undo = p_stack[p_sp++];
 
 
 
      undo.move = move::NULL_;
 
 
 
      undo.copy = p_copy;
 
 
 
      flip_turn();
 
      p_copy.ep_sq = square::NONE;
 
      p_copy.moves = 0; // HACK: conversion
 
      p_copy.recap = square::NONE;
 
 
 
      update();
 
   }
 
 
 
   void undo_null() {
 
 
 
      assert(p_sp > 0);
 
      const Undo & undo = p_stack[--p_sp];
 
 
 
      assert(undo.move == move::NULL_);
 
 
 
      flip_turn();
 
      p_copy = undo.copy;
 
 
 
      update();
 
   }
 
 
 
};
 
 
 
}
 
 
 
namespace attack {
 
 
 
struct Attacks {
 
   int size;
 
   int square[2];
 
   bit_t avoid;
 
   bit_t pinned;
 
};
 
 
 
const int Pawn_Move[side::SIZE] = { +1, -1 };
 
const int Pawn_Attack[side::SIZE][2] = { { -15, +17 }, { -17, +15 } };
 
 
 
const int Knight_Inc[] = { -33, -31, -18, -14, +14, +18, +31, +33, 0 };
 
const int Bishop_Inc[] = { -17, -15, +15, +17, 0 };
 
const int Rook_Inc[]   = { -16, -1, +1, +16, 0 };
 
const int Queen_Inc[]  = { -17, -16, -15, -1, +1, +15, +16, +17, 0 };
 
 
 
const int * Piece_Inc[piece::SIZE] = { NULL, Knight_Inc, Bishop_Inc, Rook_Inc, Queen_Inc, Queen_Inc, NULL };
 
 
 
bit_t Pawn_Moves[side::SIZE][square::SIZE];
 
bit_t Pawn_Attacks[side::SIZE][square::SIZE];
 
bit_t Piece_Attacks[piece::SIZE][square::SIZE];
 
bit_t Blockers[piece::SIZE][square::SIZE];
 
 
 
bit_t Between[square::SIZE][square::SIZE];
 
bit_t Behind[square::SIZE][square::SIZE];
 
 
 
bool line_is_empty(int f, int t, const board::Board & bd) {
 
   return (bd.all() & Between[f][t]) == 0;
 
}
 
 
 
bit_t ray(int f, int t) {
 
   return Between[f][t] | Behind[f][t]; // HACK: t should be included
 
}
 
 
 
bool pawn_move(int sd, int f, int t, const board::Board & bd) {
 
   assert(sd < side::SIZE);
 
   return bit::is_set(Pawn_Moves[sd][f], t) && line_is_empty(f, t, bd);
 
}
 
 
 
bool pawn_attack(int sd, int f, int t) {
 
   assert(sd < side::SIZE);
 
   return bit::is_set(Pawn_Attacks[sd][f], t);
 
}
 
 
 
bool piece_attack(int pc, int f, int t, const board::Board & bd) {
 
   assert(pc != piece::PAWN);
 
   return bit::is_set(Piece_Attacks[pc][f], t) && line_is_empty(f, t, bd);
 
}
 
 
 
bool attack(int pc, int sd, int f, int t, const board::Board & bd) {
 
   assert(sd < side::SIZE);
 
   if (pc == piece::PAWN) {
 
      return pawn_attack(sd, f, t);
 
   } else {
 
      return piece_attack(pc, f, t, bd);
 
   }
 
}
 
 
 
bit_t pawn_moves_from(int sd, const board::Board & bd) { // for pawn mobility
 
 
 
   assert(sd < side::SIZE);
 
 
 
   bit_t fs = bd.piece(piece::PAWN, sd);
 
 
 
   if (sd == side::WHITE) {
 
      return fs << 1;
 
   } else {
 
      return fs >> 1;
 
   }
 
}
 
 
 
bit_t pawn_moves_to(int sd, bit_t ts, const board::Board & bd) {
 
 
 
   assert(sd < side::SIZE);
 
   assert((bd.all() & ts) == 0);
 
 
 
   bit_t pawns = bd.piece(piece::PAWN, sd);
 
   bit_t empty = bd.empty();
 
 
 
   bit_t fs = 0;
 
 
 
   if (sd == side::WHITE) {
 
      fs |= (ts >> 1);
 
      fs |= (ts >> 2) & (empty >> 1) & bit::rank(square::RANK_2);
 
   } else {
 
      fs |= (ts << 1);
 
      fs |= (ts << 2) & (empty << 1) & bit::rank(square::RANK_7);
 
   }
 
 
 
   return pawns & fs;
 
}
 
 
 
bit_t pawn_attacks_from(int sd, const board::Board & bd) {
 
 
 
   assert(sd < side::SIZE);
 
 
 
   bit_t fs = bd.piece(piece::PAWN, sd);
 
 
 
   if (sd == side::WHITE) {
 
      return (fs >> 7) | (fs << 9);
 
   } else {
 
      return (fs >> 9) | (fs << 7);
 
   }
 
}
 
 
 
bit_t pawn_attacks_tos(int sd, bit_t ts) {
 
 
 
   assert(sd < side::SIZE);
 
 
 
   if (sd == side::WHITE) {
 
      return (ts >> 9) | (ts << 7);
 
   } else {
 
      return (ts >> 7) | (ts << 9);
 
   }
 
}
 
 
 
bit_t pawn_attacks_from(int sd, int f) {
 
   assert(sd < side::SIZE);
 
   return Pawn_Attacks[sd][f];
 
}
 
 
 
bit_t pawn_attacks_to(int sd, int t) {
 
   assert(sd < side::SIZE);
 
   return pawn_attacks_from(side::opposit(sd), t);
 
}
 
 
 
bit_t piece_attacks_from(int pc, int f, const board::Board & bd) {
 
 
 
   assert(pc != piece::PAWN);
 
 
 
   bit_t ts = Piece_Attacks[pc][f];
 
 
 
   for (bit_t b = bd.all() & Blockers[pc][f]; b != 0; b = bit::rest(b)) {
 
      int sq = bit::first(b);
 
      ts &= ~Behind[f][sq];
 
   }
 
 
 
   return ts;
 
}
 
 
 
bit_t piece_attacks_to(int pc, int t, const board::Board & bd) {
 
   assert(pc != piece::PAWN);
 
   return piece_attacks_from(pc, t, bd);
 
}
 
 
 
bit_t piece_moves_from(int pc, int sd, int f, const board::Board & bd) {
 
   if (pc == piece::PAWN) {
 
      assert(false); // TODO: blockers
 
      return Pawn_Moves[sd][f];
 
   } else {
 
      return piece_attacks_from(pc, f, bd);
 
   }
 
}
 
 
 
bit_t attacks_from(int pc, int sd, int f, const board::Board & bd) {
 
   if (pc == piece::PAWN) {
 
      return Pawn_Attacks[sd][f];
 
   } else {
 
      return piece_attacks_from(pc, f, bd);
 
   }
 
}
 
 
 
bit_t attacks_to(int pc, int sd, int t, const board::Board & bd) {
 
   return attacks_from(pc, side::opposit(sd), t, bd); // HACK for pawns
 
}
 
 
 
bit_t pseudo_attacks_from(int pc, int sd, int f) {
 
   if (pc == piece::PAWN) {
 
      return Pawn_Attacks[sd][f];
 
   } else {
 
      return Piece_Attacks[pc][f];
 
   }
 
}
 
 
 
bit_t pseudo_attacks_to(int pc, int sd, int t) {
 
   return pseudo_attacks_from(pc, side::opposit(sd), t); // HACK for pawns
 
}
 
 
 
bit_t slider_pseudo_attacks_to(int sd, int t, const board::Board & bd) {
 
 
 
   assert(sd < side::SIZE);
 
 
 
   bit_t b = 0;
 
   b |= bd.piece(piece::BISHOP, sd) & Piece_Attacks[piece::BISHOP][t];
 
   b |= bd.piece(piece::ROOK,   sd) & Piece_Attacks[piece::ROOK][t];
 
   b |= bd.piece(piece::QUEEN,  sd) & Piece_Attacks[piece::QUEEN][t];
 
 
 
   return b;
 
}
 
 
 
bool attack_behind(int f, int t, int sd, const board::Board & bd) {
 
 
 
   assert(bd.square(t) != piece::NONE);
 
 
 
   bit_t behind = Behind[f][t];
 
   if (behind == 0) return false;
 
 
 
   for (bit_t b = slider_pseudo_attacks_to(sd, t, bd) & behind; b != 0; b = bit::rest(b)) {
 
 
 
      int sq = bit::first(b);
 
 
 
      if (bit::single(bd.all() & Between[sq][f])) {
 
         return true;
 
      }
 
   }
 
 
 
   return false;
 
}
 
 
 
bool is_attacked(int t, int sd, const board::Board & bd) {
 
 
 
   // non-sliders
 
 
 
   if ((bd.piece(piece::PAWN, sd) & Pawn_Attacks[side::opposit(sd)][t]) != 0) { // HACK
 
      return true;
 
   }
 
 
 
   if ((bd.piece(piece::KNIGHT, sd) & Piece_Attacks[piece::KNIGHT][t]) != 0) {
 
      return true;
 
   }
 
 
 
   if ((bd.piece(piece::KING, sd) & Piece_Attacks[piece::KING][t]) != 0) {
 
      return true;
 
   }
 
 
 
   // sliders
 
 
 
   for (bit_t b = slider_pseudo_attacks_to(sd, t, bd); b != 0; b = bit::rest(b)) {
 
 
 
      int f = bit::first(b);
 
 
 
      if ((bd.all() & Between[f][t]) == 0) {
 
         return true;
 
      }
 
   }
 
 
 
   return false;
 
}
 
 
 
bit_t pinned_by(int t, int sd, const board::Board & bd) {
 
 
 
   bit_t pinned = 0;
 
 
 
   for (bit_t b = slider_pseudo_attacks_to(sd, t, bd); b != 0; b = bit::rest(b)) {
 
 
 
      int f = bit::first(b);
 
 
 
      bit_t bb = bd.all() & Between[f][t];
 
 
 
      if (bb != 0 && bit::single(bb)) {
 
         pinned |= bb;
 
      }
 
   }
 
 
 
   return pinned;
 
}
 
 
 
void init_attacks(Attacks & attacks, int sd, const board::Board & bd) { // for strictly-legal moves
 
 
 
   int atk = side::opposit(sd);
 
   int def = sd;
 
 
 
   int t = bd.king(def);
 
 
 
   attacks.size   = 0;
 
   attacks.avoid  = 0;
 
   attacks.pinned = 0;
 
 
 
   // non-sliders
 
 
 
   {
 
      bit_t b = 0;
 
      b |= bd.piece(piece::PAWN,   atk) & Pawn_Attacks[def][t]; // HACK
 
      b |= bd.piece(piece::KNIGHT, atk) & Piece_Attacks[piece::KNIGHT][t];
 
 
 
      if (b != 0) {
 
         assert(bit::single(b));
 
         assert(attacks.size < 2);
 
         attacks.square[attacks.size++] = bit::first(b);
 
      }
 
   }
 
 
 
   // sliders
 
 
 
   for (bit_t b = slider_pseudo_attacks_to(atk, t, bd); b != 0; b = bit::rest(b)) {
 
 
 
      int f = bit::first(b);
 
 
 
      bit_t bb = bd.all() & Between[f][t];
 
 
 
      if (bb == 0) {
 
         assert(attacks.size < 2);
 
         attacks.square[attacks.size++] = f;
 
         attacks.avoid |= ray(f, t);
 
      } else if (bit::single(bb)) {
 
         attacks.pinned |= bb;
 
      }
 
   }
 
}
 
 
 
void init_attacks(Attacks & attacks, const board::Board & bd) {
 
   init_attacks(attacks, bd.turn(), bd);
 
}
 
 
 
bool is_legal(const board::Board & bd) {
 
 
 
   int atk = bd.turn();
 
   int def = side::opposit(atk);
 
 
 
   return !is_attacked(bd.king(def), atk, bd);
 
}
 
 
 
bool is_in_check(const board::Board & bd) {
 
 
 
   int atk = bd.turn();
 
   int def = side::opposit(atk);
 
 
 
   return is_attacked(bd.king(atk), def, bd);
 
}
 
 
 
bit_t pawn_moves_debug(int sd, int sq) {
 
 
 
   assert(sd < side::SIZE);
 
 
 
   bit_t b = 0;
 
 
 
   int f = square::to_88(sq);
 
   int inc = Pawn_Move[sd];
 
 
 
   int t = f + inc;
 
 
 
   if (square::is_valid_88(t)) {
 
      bit::set(b, square::from_88(t));
 
   }
 
 
 
   if (square::rank(sq, sd) == square::RANK_2) {
 
      t += inc;
 
      assert(square::is_valid_88(t));
 
      bit::set(b, square::from_88(t));
 
   }
 
 
 
   return b;
 
}
 
 
 
bit_t pawn_attacks_debug(int sd, int sq) {
 
 
 
   assert(sd < side::SIZE);
 
 
 
   bit_t b = 0;
 
 
 
   int f = square::to_88(sq);
 
 
 
   for (int dir = 0; dir < 2; dir++) {
 
      int t = f + Pawn_Attack[sd][dir];
 
      if (square::is_valid_88(t)) {
 
         bit::set(b, square::from_88(t));
 
      }
 
   }
 
 
 
   return b;
 
}
 
 
 
bit_t piece_attacks_debug(int pc, int sq) {
 
 
 
   assert(pc != piece::PAWN);
 
 
 
   bit_t b = 0;
 
 
 
   int f = square::to_88(sq);
 
 
 
   for (int dir = 0; true; dir++) {
 
 
 
      int inc = Piece_Inc[pc][dir];
 
      if (inc == 0) break;
 
 
 
      if (piece::is_slider(pc)) {
 
 
 
         for (int t = f + inc; square::is_valid_88(t); t += inc) {
 
            bit::set(b, square::from_88(t));
 
         }
 
 
 
      } else {
 
 
 
         int t = f + inc;
 
 
 
         if (square::is_valid_88(t)) {
 
            bit::set(b, square::from_88(t));
 
         }
 
      }
 
   }
 
 
 
   return b;
 
}
 
 
 
int delta_inc(int f, int t) {
 
 
 
   for (int dir = 0; dir < 8; dir++) {
 
 
 
      int inc = Queen_Inc[dir];
 
 
 
      for (int sq = f + inc; square::is_valid_88(sq); sq += inc) {
 
         if (sq == t) {
 
            return inc;
 
         }
 
      }
 
   }
 
 
 
   return 0;
 
}
 
 
 
bit_t between_debug(int f, int t) {
 
 
 
   f = square::to_88(f);
 
   t = square::to_88(t);
 
 
 
   bit_t b = 0;
 
 
 
   int inc = delta_inc(f, t);
 
 
 
   if (inc != 0) {
 
      for (int sq = f + inc; sq != t; sq += inc) {
 
         bit::set(b, square::from_88(sq));
 
      }
 
   }
 
 
 
   return b;
 
}
 
 
 
bit_t behind_debug(int f, int t) {
 
 
 
   f = square::to_88(f);
 
   t = square::to_88(t);
 
 
 
   bit_t b = 0;
 
 
 
   int inc = delta_inc(f, t);
 
 
 
   if (inc != 0) {
 
      for (int sq = t + inc; square::is_valid_88(sq); sq += inc) {
 
         bit::set(b, square::from_88(sq));
 
      }
 
   }
 
 
 
   return b;
 
}
 
 
 
bit_t blockers_debug(int pc, int f) {
 
 
 
   assert(pc != piece::PAWN);
 
 
 
   bit_t b = 0;
 
 
 
   bit_t attacks = piece_attacks_debug(pc, f);
 
 
 
   for (bit_t bb = attacks; bb != 0; bb = bit::rest(bb)) {
 
      int sq = bit::first(bb);
 
      if ((attacks & behind_debug(f, sq)) != 0) {
 
         bit::set(b, sq);
 
      }
 
   }
 
 
 
   return b;
 
}
 
 
 
void init() {
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      for (int sq = 0; sq < square::SIZE; sq++) {
 
         Pawn_Moves[sd][sq] = pawn_moves_debug(sd, sq);
 
         Pawn_Attacks[sd][sq] = pawn_attacks_debug(sd, sq);
 
      }
 
   }
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::KING; pc++) {
 
      for (int sq = 0; sq < square::SIZE; sq++) {
 
         Piece_Attacks[pc][sq] = piece_attacks_debug(pc, sq);
 
         Blockers[pc][sq] = blockers_debug(pc, sq);
 
      }
 
   }
 
 
 
   for (int f = 0; f < square::SIZE; f++) {
 
      for (int t = 0; t < square::SIZE; t++) {
 
         Between[f][t] = between_debug(f, t);
 
         Behind[f][t]  = behind_debug(f, t);
 
      }
 
   }
 
}
 
 
 
}
 
 
 
namespace gen {
 
 
 
class List {
 
 
 
private:
 
 
 
   static const int SIZE = 256;
 
 
 
   int p_size;
 
   uint32 p_pair[SIZE];
 
 
 
   void move_to(int pf, int pt) {
 
 
 
      assert(pt <= pf && pf < p_size);
 
 
 
      uint32 p = p_pair[pf];
 
 
 
      for (int i = pf; i > pt; i--) {
 
         p_pair[i] = p_pair[i - 1];
 
      }
 
 
 
      p_pair[pt] = p;
 
   }
 
 
 
   void add_pair(uint32 p) {
 
      assert(p_size < SIZE);
 
      p_pair[p_size++] = p;
 
   }
 
 
 
   uint32 pair(int pos) const {
 
      assert(pos < p_size);
 
      return p_pair[pos];
 
   }
 
 
 
public:
 
 
 
   List() {
 
      clear();
 
   }
 
 
 
   void operator=(const List & ml) {
 
 
 
      clear();
 
 
 
      for (int pos = 0; pos < ml.size(); pos++) {
 
         uint32 p = ml.pair(pos);
 
         add_pair(p);
 
      }
 
   }
 
 
 
   void clear() {
 
      p_size = 0;
 
   }
 
 
 
   void add(int mv, int sc = 0) {
 
      assert(mv >= 0 && mv < move::SIZE);
 
      assert(sc >= 0 && sc < move::SCORE_SIZE);
 
      assert(!contain(mv));
 
      add_pair((sc << move::BITS) | mv);
 
   }
 
 
 
   void set_score(int pos, int sc) {
 
      assert(pos < p_size);
 
      assert(sc >= 0 && sc < move::SCORE_SIZE);
 
      p_pair[pos] = (sc << move::BITS) | move(pos);
 
      assert(score(pos) == sc);
 
   }
 
 
 
   void move_to_front(int pos) {
 
      move_to(pos, 0);
 
   }
 
 
 
   void sort() { // insertion sort
 
 
 
      for (int i = 1; i < p_size; i++) {
 
 
 
         uint32 p = p_pair[i];
 
 
 
         int j;
 
 
 
         for (j = i; j > 0 && p_pair[j - 1] < p; j--) {
 
            p_pair[j] = p_pair[j - 1];
 
         }
 
 
 
         p_pair[j] = p;
 
      }
 
 
 
      for (int i = 0; i < p_size - 1; i++) {
 
         assert(p_pair[i] >= p_pair[i + 1]);
 
      }
 
   }
 
 
 
   int size() const {
 
      return p_size;
 
   }
 
 
 
   int move(int pos) const {
 
      return pair(pos) & move::MASK;
 
   }
 
 
 
   int score(int pos) const {
 
      return pair(pos) >> move::BITS;
 
   }
 
 
 
   bool contain(int mv) const {
 
 
 
      for (int pos = 0; pos < size(); pos++) {
 
         if (move(pos) == mv) {
 
            return true;
 
         }
 
      }
 
 
 
      return false;
 
   }
 
 
 
};
 
 
 
void add_pawn_move(List & ml, int f, int t, const board::Board & bd) {
 
 
 
   assert(bd.square(f) == piece::PAWN);
 
 
 
   int pc = bd.square(f);
 
   int cp = bd.square(t);
 
 
 
   if (square::is_promotion(t)) {
 
      ml.add(move::make(f, t, pc, cp, piece::QUEEN));
 
      ml.add(move::make(f, t, pc, cp, piece::KNIGHT));
 
      ml.add(move::make(f, t, pc, cp, piece::ROOK));
 
      ml.add(move::make(f, t, pc, cp, piece::BISHOP));
 
   } else {
 
      ml.add(move::make(f, t, pc, cp));
 
   }
 
}
 
 
 
void add_piece_move(List & ml, int f, int t, const board::Board & bd) {
 
   assert(bd.square(f) != piece::PAWN);
 
   ml.add(move::make(f, t, bd.square(f), bd.square(t)));
 
}
 
 
 
void add_move(List & ml, int f, int t, const board::Board & bd) {
 
   if (bd.square(f) == piece::PAWN) {
 
      add_pawn_move(ml, f, t, bd);
 
   } else {
 
      add_piece_move(ml, f, t, bd);
 
   }
 
}
 
 
 
void add_piece_moves_from(List & ml, int f, bit_t ts, const board::Board & bd) {
 
 
 
   int pc = bd.square(f);
 
 
 
   for (bit_t b = attack::piece_attacks_from(pc, f, bd) & ts; b != 0; b = bit::rest(b)) {
 
      int t = bit::first(b);
 
      add_piece_move(ml, f, t, bd);
 
   }
 
}
 
 
 
void add_captures_to(List & ml, int sd, int t, const board::Board & bd) {
 
 
 
   for (int pc = piece::PAWN; pc <= piece::KING; pc++) {
 
      for (bit_t b = bd.piece(pc, sd) & attack::attacks_to(pc, sd, t, bd); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         add_move(ml, f, t, bd);
 
      }
 
   }
 
}
 
 
 
void add_captures_to_no_king(List & ml, int sd, int t, const board::Board & bd) { // for evasions
 
 
 
   for (int pc = piece::PAWN; pc <= piece::QUEEN; pc++) { // skip king
 
      for (bit_t b = bd.piece(pc, sd) & attack::attacks_to(pc, sd, t, bd); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         add_move(ml, f, t, bd);
 
      }
 
   }
 
}
 
 
 
void add_pawn_captures(List & ml, int sd, bit_t ts, const board::Board & bd) {
 
 
 
   bit_t pawns = bd.piece(piece::PAWN, sd);
 
   ts &= bd.side(side::opposit(sd)); // not needed
 
 
 
   if (sd == side::WHITE) {
 
 
 
      for (bit_t b = (ts << 7) & pawns; b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f - 7;
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
      for (bit_t b = (ts >> 9) & pawns; b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f + 9;
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
   } else {
 
 
 
      for (bit_t b = (ts << 9) & pawns; b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f - 9;
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
      for (bit_t b = (ts >> 7) & pawns; b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f + 7;
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
   }
 
}
 
 
 
void add_promotions(List & ml, int sd, bit_t ts, const board::Board & bd) {
 
 
 
   bit_t pawns = bd.piece(piece::PAWN, sd);
 
 
 
   if (sd == side::WHITE) {
 
 
 
      for (bit_t b = pawns & (ts >> 1) & bit::rank(square::RANK_7); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f + 1;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
   } else {
 
 
 
      for (bit_t b = pawns & (ts << 1) & bit::rank(square::RANK_2); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f - 1;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
   }
 
}
 
 
 
void add_promotions(List & ml, int sd, const board::Board & bd) {
 
   add_promotions(ml, sd, bd.empty(), bd);
 
}
 
 
 
void add_pawn_quiets(List & ml, int sd, bit_t ts, const board::Board & bd) {
 
 
 
   bit_t pawns = bd.piece(piece::PAWN, sd);
 
   bit_t empty = bd.empty();
 
 
 
   if (sd == side::WHITE) {
 
 
 
      for (bit_t b = pawns & (ts >> 1) & ~bit::rank(square::RANK_7); b != 0; b = bit::rest(b)) { // don't generate promotions
 
         int f = bit::first(b);
 
         int t = f + 1;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(!square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
      for (bit_t b = pawns & (ts >> 2) & (empty >> 1) & bit::rank(square::RANK_2); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f + 2;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(!square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
   } else {
 
 
 
      for (bit_t b = pawns & (ts << 1) & ~bit::rank(square::RANK_2); b != 0; b = bit::rest(b)) { // don't generate promotions
 
         int f = bit::first(b);
 
         int t = f - 1;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(!square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
 
 
      for (bit_t b = pawns & (ts << 2) & (empty << 1) & bit::rank(square::RANK_7); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         int t = f - 2;
 
         assert(bd.square(t) == piece::NONE);
 
         assert(!square::is_promotion(t));
 
         add_pawn_move(ml, f, t, bd);
 
      }
 
   }
 
}
 
 
 
void add_pawn_pushes(List & ml, int sd, const board::Board & bd) {
 
 
 
   bit_t ts = 0;
 
 
 
   if (sd == side::WHITE) {
 
 
 
      ts |= bit::rank(square::RANK_7);
 
      ts |= bit::rank(square::RANK_6) & ~attack::pawn_attacks_from(side::BLACK, bd) & (~bd.piece(piece::PAWN) >> 1); // HACK: direct access
 
 
 
   } else {
 
 
 
      ts |= bit::rank(square::RANK_2);
 
      ts |= bit::rank(square::RANK_3) & ~attack::pawn_attacks_from(side::WHITE, bd) & (~bd.piece(piece::PAWN) << 1); // HACK: direct access
 
   }
 
 
 
   add_pawn_quiets(ml, sd, ts & bd.empty(), bd);
 
}
 
 
 
void add_en_passant(List & ml, int sd, const board::Board & bd) {
 
 
 
   int t = bd.ep_sq();
 
 
 
   if (t != square::NONE) {
 
 
 
      bit_t fs = bd.piece(piece::PAWN, sd) & attack::Pawn_Attacks[side::opposit(sd)][t];
 
 
 
      for (bit_t b = fs; b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         ml.add(move::make(f, t, piece::PAWN, piece::PAWN));
 
      }
 
   }
 
}
 
 
 
bool can_castle(int sd, int wg, const board::Board & bd) {
 
 
 
   int index = castling::index(sd, wg);
 
 
 
   if (castling::flag(bd.flags(), index)) {
 
 
 
      int kf = castling::info[index].kf;
 
      // int kt = castling::info[index].kt;
 
      int rf = castling::info[index].rf;
 
      int rt = castling::info[index].rt;
 
 
 
      assert(bd.square_is(kf, piece::KING, sd));
 
      assert(bd.square_is(rf, piece::ROOK, sd));
 
 
 
      return attack::line_is_empty(kf, rf, bd) && !attack::is_attacked(rt, side::opposit(sd), bd);
 
   }
 
 
 
   return false;
 
}
 
 
 
void add_castling(List & ml, int sd, const board::Board & bd) {
 
 
 
   for (int wg = 0; wg < wing::SIZE; wg++) {
 
      if (can_castle(sd, wg, bd)) {
 
         int index = castling::index(sd, wg);
 
         add_piece_move(ml, castling::info[index].kf, castling::info[index].kt, bd);
 
      }
 
   }
 
}
 
 
 
void add_piece_moves(List & ml, int sd, bit_t ts, const board::Board & bd) {
 
 
 
   assert(ts != 0);
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::KING; pc++) {
 
      for (bit_t b = bd.piece(pc, sd); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         add_piece_moves_from(ml, f, ts, bd);
 
      }
 
   }
 
}
 
 
 
void add_piece_moves_no_king(List & ml, int sd, bit_t ts, const board::Board & bd) { // for evasions
 
 
 
   assert(ts != 0);
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::QUEEN; pc++) { // skip king
 
      for (bit_t b = bd.piece(pc, sd); b != 0; b = bit::rest(b)) {
 
         int f = bit::first(b);
 
         add_piece_moves_from(ml, f, ts, bd);
 
      }
 
   }
 
}
 
 
 
void add_piece_moves_rare(List & ml, int sd, bit_t ts, const board::Board & bd) { // for captures
 
 
 
   assert(ts != 0);
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::KING; pc++) {
 
 
 
      for (bit_t b = bd.piece(pc, sd); b != 0; b = bit::rest(b)) {
 
 
 
         int f = bit::first(b);
 
 
 
         for (bit_t bb = attack::pseudo_attacks_from(pc, sd, f) & ts; bb != 0; bb = bit::rest(bb)) {
 
 
 
            int t = bit::first(bb);
 
 
 
            if (attack::line_is_empty(f, t, bd)) {
 
               add_piece_move(ml, f, t, bd);
 
            }
 
         }
 
      }
 
   }
 
}
 
 
 
void add_captures(List & ml, int sd, const board::Board & bd) {
 
 
 
   bit_t ts = bd.side(side::opposit(sd));
 
 
 
   add_pawn_captures(ml, sd, ts, bd);
 
   add_piece_moves_rare(ml, sd, ts, bd);
 
   add_en_passant(ml, sd, bd);
 
}
 
 
 
void add_captures_mvv_lva(List & ml, int sd, const board::Board & bd) { // unused
 
 
 
   for (int pc = piece::QUEEN; pc >= piece::PAWN; pc--) {
 
      for (bit_t b = bd.piece(pc, side::opposit(sd)); b != 0; b = bit::rest(b)) {
 
         int t = bit::first(b);
 
         add_captures_to(ml, sd, t, bd);
 
      }
 
   }
 
 
 
   add_en_passant(ml, sd, bd);
 
}
 
 
 
bool is_move(int mv, const board::Board & bd) { // for TT-move legality
 
 
 
   int sd = bd.turn();
 
 
 
   int f = move::from(mv);
 
   int t = move::to(mv);
 
 
 
   int pc = move::piece(mv);
 
   int cp = move::cap(mv);
 
 
 
   if (!(bd.square(f) == pc && bd.square_side(f) == sd)) {
 
      return false;
 
   }
 
 
 
   if (bd.square(t) != piece::NONE && bd.square_side(t) == sd) {
 
      return false;
 
   }
 
 
 
   if (pc == piece::PAWN && t == bd.ep_sq()) {
 
      if (cp != piece::PAWN) {
 
         return false;
 
      }
 
   } else if (bd.square(t) != cp) {
 
      return false;
 
   }
 
 
 
   if (cp == piece::KING) {
 
      return false;
 
   }
 
 
 
   if (pc == piece::PAWN) {
 
 
 
      // TODO
 
 
 
      return true;
 
 
 
   } else {
 
 
 
      // TODO: castling
 
 
 
      // return attack::piece_attack(pc, f, t, bd);
 
 
 
      return true;
 
   }
 
 
 
   assert(false);
 
}
 
 
 
bool is_quiet_move(int mv, const board::Board & bd) { // for killer legality
 
 
 
   int sd = bd.turn();
 
 
 
   int f = move::from(mv);
 
   int t = move::to(mv);
 
 
 
   int pc = move::piece(mv);
 
   assert(move::cap(mv) == piece::NONE);
 
   assert(move::prom(mv) == piece::NONE);
 
 
 
   if (!(bd.square(f) == pc && bd.square_side(f) == sd)) {
 
      return false;
 
   }
 
 
 
   if (bd.square(t) != piece::NONE) {
 
      return false;
 
   }
 
 
 
   if (pc == piece::PAWN) {
 
 
 
      int inc = square::pawn_inc(sd);
 
 
 
      if (false) {
 
      } else if (t - f == inc && !square::is_promotion(t)) {
 
         return true;
 
      } else if (t - f == inc * 2 && square::rank(f, sd) == square::RANK_2) {
 
         return bd.square(f + inc) == piece::NONE;
 
      } else {
 
         return false;
 
      }
 
 
 
   } else {
 
 
 
      // TODO: castling
 
 
 
      return attack::piece_attack(pc, f, t, bd);
 
   }
 
 
 
   assert(false);
 
}
 
 
 
void add_quiets(List & ml, int sd, const board::Board & bd) {
 
   add_castling(ml, sd, bd);
 
   add_piece_moves(ml, sd, bd.empty(), bd);
 
   add_pawn_quiets(ml, sd, bd.empty(), bd);
 
}
 
 
 
void add_evasions(List & ml, int sd, const board::Board & bd, const attack::Attacks & attacks) {
 
 
 
   assert(attacks.size > 0);
 
 
 
   int king = bd.king(sd);
 
 
 
   add_piece_moves_from(ml, king, ~bd.side(sd) & ~attacks.avoid, bd);
 
 
 
   if (attacks.size == 1) {
 
 
 
      int t = attacks.square[0];
 
 
 
      add_captures_to_no_king(ml, sd, t, bd);
 
      add_en_passant(ml, sd, bd);
 
 
 
      bit_t ts = attack::Between[king][t];
 
      assert(attack::line_is_empty(king, t, bd));
 
 
 
      if (ts != 0) {
 
         add_pawn_quiets(ml, sd, ts, bd);
 
         add_promotions(ml, sd, ts, bd);
 
         add_piece_moves_no_king(ml, sd, ts, bd);
 
      }
 
   }
 
}
 
 
 
void add_evasions(List & ml, int sd, const board::Board & bd) {
 
   attack::Attacks attacks;
 
   attack::init_attacks(attacks, sd, bd);
 
   add_evasions(ml, sd, bd, attacks);
 
}
 
 
 
void add_checks(List & ml, int sd, const board::Board & bd) {
 
 
 
   int atk = sd;
 
   int def = side::opposit(sd);
 
 
 
   int king = bd.king(def);
 
   bit_t pinned = attack::pinned_by(king, atk, bd);
 
   bit_t empty = bd.empty();
 
   empty &= ~attack::pawn_attacks_from(side::opposit(sd), bd); // pawn-safe
 
 
 
   // discovered checks
 
 
 
   for (bit_t fs = bd.pieces(atk) & pinned; fs != 0; fs = bit::rest(fs)) { // TODO: pawns
 
      int f = bit::first(fs);
 
      bit_t ts = empty & ~attack::ray(king, f); // needed only for pawns
 
      add_piece_moves_from(ml, f, ts, bd);
 
   }
 
 
 
   // direct checks, pawns
 
 
 
   {
 
      bit_t ts = attack::pseudo_attacks_to(piece::PAWN, sd, king) & empty;
 
 
 
      add_pawn_quiets(ml, sd, ts, bd);
 
   }
 
 
 
   // direct checks, knights
 
 
 
   {
 
      int pc = piece::KNIGHT;
 
 
 
      bit_t attacks = attack::pseudo_attacks_to(pc, sd, king) & empty;
 
 
 
      for (bit_t b = bd.piece(pc, sd) & ~pinned; b != 0; b = bit::rest(b)) {
 
 
 
         int f = bit::first(b);
 
 
 
         bit_t moves = attack::pseudo_attacks_from(pc, sd, f);
 
 
 
         for (bit_t bb = moves & attacks; bb != 0; bb = bit::rest(bb)) {
 
            int t = bit::first(bb);
 
            add_piece_move(ml, f, t, bd);
 
         }
 
      }
 
   }
 
 
 
   // direct checks, sliders
 
 
 
   for (int pc = piece::BISHOP; pc <= piece::QUEEN; pc++) {
 
 
 
      bit_t attacks = attack::pseudo_attacks_to(pc, sd, king) & empty;
 
 
 
      for (bit_t b = bd.piece(pc, sd) & ~pinned; b != 0; b = bit::rest(b)) {
 
 
 
         int f = bit::first(b);
 
 
 
         bit_t moves = attack::pseudo_attacks_from(pc, sd, f);
 
 
 
         for (bit_t bb = moves & attacks; bb != 0; bb = bit::rest(bb)) {
 
 
 
            int t = bit::first(bb);
 
 
 
            if (attack::line_is_empty(f, t, bd) && attack::line_is_empty(t, king, bd)) {
 
               add_piece_move(ml, f, t, bd);
 
            }
 
         }
 
      }
 
   }
 
}
 
 
 
bool is_legal_debug(int mv, board::Board & bd) { // HACK: duplicate from Move_Type
 
 
 
   bd.move(mv);
 
   bool b = attack::is_legal(bd);
 
   bd.undo();
 
 
 
   return b;
 
}
 
 
 
void gen_moves_debug(List & ml, const board::Board & bd) {
 
 
 
   ml.clear();
 
 
 
   int sd = bd.turn();
 
 
 
   if (attack::is_in_check(bd)) {
 
      add_evasions(ml, sd, bd);
 
   } else {
 
      add_captures(ml, sd, bd);
 
      add_promotions(ml, sd, bd);
 
      add_quiets(ml, sd, bd);
 
   }
 
}
 
 
 
void filter_legals(List & dst, const List & src, board::Board & bd) {
 
 
 
   dst.clear();
 
 
 
   for (int pos = 0; pos < src.size(); pos++) {
 
 
 
      int mv = src.move(pos);
 
 
 
      if (is_legal_debug(mv, bd)) {
 
         dst.add(mv);
 
      }
 
   }
 
}
 
 
 
void gen_legals(List & ml, board::Board & bd) {
 
 
 
   List pseudos;
 
   gen_moves_debug(pseudos, bd);
 
 
 
   filter_legals(ml, pseudos, bd);
 
}
 
 
 
void gen_legal_evasions(List & ml, board::Board & bd) {
 
 
 
   int sd = bd.turn();
 
 
 
   attack::Attacks attacks;
 
   attack::init_attacks(attacks, sd, bd);
 
 
 
   if (attacks.size == 0) {
 
      ml.clear();
 
      return;
 
   }
 
 
 
   List pseudos;
 
   add_evasions(pseudos, sd, bd, attacks);
 
 
 
   filter_legals(ml, pseudos, bd);
 
}
 
 
 
}
 
 
 
namespace score {
 
 
 
enum {
 
   NONE     = -10000,
 
   MIN      =  -9999,
 
   EVAL_MIN =  -8999,
 
   EVAL_MAX =  +8999,
 
   MAX      =  +9999,
 
   MATE     = +10000,
 
};
 
 
 
enum {
 
   FLAGS_NONE  = 0,
 
   FLAGS_LOWER = 1 << 0,
 
   FLAGS_UPPER = 1 << 1,
 
   FLAGS_EXACT = FLAGS_LOWER | FLAGS_UPPER,
 
};
 
 
 
bool is_mate(int sc) {
 
   return sc < EVAL_MIN || sc > EVAL_MAX;
 
}
 
 
 
int signed_mate(int sc) {
 
   if (sc < EVAL_MIN) { // -MATE
 
      return -(MATE + sc) / 2;
 
   } else if (sc > EVAL_MAX) { // +MATE
 
      return (MATE - sc + 1) / 2;
 
   } else {
 
      assert(false);
 
      return 0;
 
   }
 
}
 
 
 
int side_score(int sc, int sd) {
 
   return (sd == side::WHITE) ? +sc : -sc;
 
}
 
 
 
int from_trans(int sc, int ply) {
 
   if (sc < EVAL_MIN) {
 
      return sc + ply;
 
   } else if (sc > EVAL_MAX) {
 
      return sc - ply;
 
   } else {
 
      return sc;
 
   }
 
}
 
 
 
int to_trans(int sc, int ply) {
 
   if (sc < EVAL_MIN) {
 
      return sc - ply;
 
   } else if (sc > EVAL_MAX) {
 
      return sc + ply;
 
   } else {
 
      return sc;
 
   }
 
}
 
 
 
int flags(int sc, int alpha, int beta) {
 
 
 
   int flags = FLAGS_NONE;
 
   if (sc > alpha) flags |= FLAGS_LOWER;
 
   if (sc < beta)  flags |= FLAGS_UPPER;
 
 
 
   return flags;
 
}
 
 
 
}
 
 
 
namespace trans {
 
 
 
struct Entry { // 16 bytes
 
   uint32 lock;
 
   uint32 move; // TODO: uint16 #
 
   uint16 pad_1;
 
   int16 score;
 
   uint8 date;
 
   int8 depth;
 
   uint8 flags;
 
   uint8 pad_2;
 
};
 
 
 
void clear_entry(Entry & entry) {
 
 
 
   assert(sizeof(Entry) == 16);
 
 
 
   entry.lock = 0;
 
   entry.move = move::NONE;
 
   entry.pad_1 = 0;
 
   entry.score = 0;
 
   entry.date = 0;
 
   entry.depth = -1;
 
   entry.flags = score::FLAGS_NONE;
 
   entry.pad_2 = 0;
 
}
 
 
 
class Table {
 
 
 
private:
 
 
 
   Entry * p_table;
 
   int p_bits;
 
   uint64 p_size;
 
   uint64 p_mask;
 
 
 
   int p_date;
 
   uint64 p_used;
 
 
 
   int size_to_bits(int size) {
 
 
 
      int bits = 0;
 
 
 
      for (uint64 entries = (uint64(size) << 20) / sizeof(Entry); entries > 1; entries /= 2) {
 
         bits++;
 
      }
 
 
 
      return bits;
 
   }
 
 
 
public:
 
 
 
   Table() {
 
 
 
      p_table = NULL;
 
      p_bits = 0;
 
      p_size = 1;
 
      p_mask = 0;
 
 
 
      p_date = 0;
 
      p_used = 0;
 
   }
 
 
 
   void set_size(int size) {
 
 
 
      int bits = size_to_bits(size);
 
      if (bits == p_bits) return;
 
 
 
      p_bits = bits;
 
      p_size = U64(1) << bits;
 
      p_mask = p_size - 1;
 
 
 
      if (p_table != NULL) {
 
         free();
 
         alloc();
 
      }
 
   }
 
 
 
   void alloc() {
 
      assert(p_table == NULL);
 
      p_table = new Entry[(unsigned int) p_size]; // Pierre-Marie Baty -- added type cast
 
      clear();
 
   }
 
 
 
   void free() {
 
      assert(p_table != NULL);
 
      delete [] p_table;
 
      p_table = NULL;
 
   }
 
 
 
   void clear() {
 
 
 
      Entry e;
 
      clear_entry(e);
 
 
 
      for (uint64 i = 0; i < p_size; i++) {
 
         p_table[i] = e;
 
      }
 
 
 
      p_date = 1;
 
      p_used = 0;
 
   }
 
 
 
   void inc_date() {
 
      p_date = (p_date + 1) % 256;
 
      p_used = 0;
 
   }
 
 
 
   void store(hash_t key, int depth, int ply, int move, int score, int flags) {
 
 
 
      assert(depth >= 0 && depth < 100);
 
      assert(move != move::NULL_);
 
      assert(score >= score::MIN && score <= score::MAX);
 
 
 
      score = score::to_trans(score, ply);
 
 
 
      uint64 index = hash::index(key) & p_mask;
 
      uint32 lock  = hash::lock(key);
 
 
 
      Entry * be = NULL;
 
      int bs = -1;
 
 
 
      for (uint64 i = 0; i < 4; i++) {
 
 
 
         uint64 idx = (index + i) & p_mask;
 
         assert(idx < p_size);
 
         Entry & entry = p_table[idx];
 
 
 
         if (entry.lock == lock) {
 
 
 
            if (entry.date != p_date) {
 
               entry.date = p_date;
 
               p_used++;
 
            }
 
 
 
            if (depth >= entry.depth) {
 
               if (move != move::NONE) entry.move = move;
 
               entry.depth = depth;
 
               entry.score = score;
 
               entry.flags = flags;
 
            } else if (entry.move == move::NONE) {
 
               entry.move = move;
 
            }
 
 
 
            return;
 
         }
 
 
 
         int sc = 99 - entry.depth; // NOTE: entry.depth can be -1
 
         if (entry.date != p_date) sc += 101;
 
         assert(sc >= 0 && sc < 202);
 
 
 
         if (sc > bs) {
 
            be = &entry;
 
            bs = sc;
 
         }
 
      }
 
 
 
      assert(be != NULL);
 
 
 
      if (be->date != p_date) p_used++;
 
 
 
      be->lock = lock;
 
      be->date = p_date;
 
      be->move = move;
 
      be->depth = depth;
 
      be->score = score;
 
      be->flags = flags;
 
   }
 
 
 
   bool retrieve(hash_t key, int depth, int ply, int & move, int & score, int & flags) {
 
 
 
      assert(depth >= 0 && depth < 100);
 
 
 
      uint64 index = hash::index(key) & p_mask;
 
      uint32 lock  = hash::lock(key);
 
 
 
      for (uint64 i = 0; i < 4; i++) {
 
 
 
         uint64 idx = (index + i) & p_mask;
 
         assert(idx < p_size);
 
         Entry & entry = p_table[idx];
 
 
 
         if (entry.lock == lock) {
 
 
 
            if (entry.date != p_date) {
 
               entry.date = p_date; // touch entry
 
               p_used++;
 
            }
 
 
 
            move = entry.move;
 
            score = score::from_trans(entry.score, ply);
 
            flags = entry.flags;
 
 
 
            if (entry.depth >= depth) {
 
               return true;
 
            } else if (score::is_mate(score)) {
 
               flags &= ~(score < 0 ? score::FLAGS_LOWER : score::FLAGS_UPPER);
 
               return true;
 
            }
 
 
 
            return false;
 
         }
 
      }
 
 
 
      return false;
 
   }
 
 
 
   int used() const {
 
      return int((p_used * 1000 + p_size / 2) / p_size);
 
   }
 
 
 
};
 
 
 
}
 
 
 
namespace engine {
 
 
 
struct Engine {
 
   int hash;
 
   bool ponder;
 
   int threads;
 
   bool log;
 
};
 
 
 
Engine engine;
 
 
 
void init() {
 
   engine.hash = 64;
 
   engine.ponder = false;
 
   engine.threads = 1;
 
   engine.log = false;
 
}
 
 
 
}
 
 
 
namespace pawn { // HACK: early declaration for pawn-pushes move type
 
 
 
bit_t passed_me[square::SIZE][side::SIZE];
 
bit_t passed_opp[square::SIZE][side::SIZE];
 
 
 
bool is_passed(int sq, int sd, const board::Board & bd) {
 
   return (bd.piece(piece::PAWN, side::opposit(sd)) & passed_opp[sq][sd]) == 0
 
       && (bd.piece(piece::PAWN, sd) & passed_me[sq][sd]) == 0;
 
}
 
 
 
int square_distance(int ks, int ps, int sd) {
 
   int prom = square::promotion(ps, sd);
 
   return square::distance(ks, prom) - square::distance(ps, prom);
 
}
 
 
 
}
 
 
 
namespace move {
 
 
 
bool is_win (int mv, const board::Board & bd);
 
 
 
class SEE {
 
 
 
private:
 
 
 
   const board::Board * p_board;
 
   int p_to;
 
   bit_t p_all;
 
 
 
   int p_val;
 
   int p_side;
 
 
 
   void init(int t, int sd) {
 
 
 
      p_to = t;
 
      p_all = p_board->all();
 
 
 
      int pc = p_board->square(t);
 
 
 
      p_val = piece::value(pc);
 
      p_side = sd;
 
   }
 
 
 
   int move(int f) {
 
 
 
      assert(bit::is_set(p_all, f));
 
      bit::clear(p_all, f);
 
 
 
      int pc = p_board->square(f);
 
      assert(pc != piece::NONE && p_board->square_side(f) == p_side);
 
 
 
      int val = p_val;
 
      p_val = piece::value(pc);
 
 
 
      if (pc == piece::PAWN && square::is_promotion(p_to)) {
 
         int delta = piece::QUEEN_VALUE - piece::PAWN_VALUE;
 
         val   += delta;
 
         p_val += delta;
 
      }
 
 
 
      if (val == piece::KING_VALUE) { // stop at king capture
 
         p_all = 0; // HACK: erase all attackers
 
      }
 
 
 
      p_side = side::opposit(p_side);
 
 
 
      return val;
 
   }
 
 
 
   int see_rec(int alpha, int beta) {
 
 
 
      assert(alpha < beta);
 
 
 
      int s0 = 0;
 
 
 
      if (s0 > alpha) {
 
         alpha = s0;
 
         if (s0 >= beta) return s0;
 
      }
 
 
 
      if (p_val <= alpha) { // FP, NOTE: fails for promotions
 
         return p_val;
 
      }
 
 
 
      int f = pick_lva();
 
 
 
      if (f == square::NONE) {
 
         return s0;
 
      }
 
 
 
      int cap = move(f); // NOTE: has side effect
 
      int s1 = cap - see_rec(cap - beta, cap - alpha);
 
 
 
      return std::max(s0, s1);
 
   }
 
 
 
   int pick_lva() const {
 
 
 
      int sd = p_side;
 
 
 
      for (int pc = piece::PAWN; pc <= piece::KING; pc++) {
 
 
 
         bit_t fs = p_board->piece(pc, sd) & attack::pseudo_attacks_to(pc, sd, p_to) & p_all;
 
 
 
         for (bit_t b = fs; b != 0; b = bit::rest(b)) {
 
 
 
            int f = bit::first(b);
 
 
 
            if ((p_all & attack::Between[f][p_to]) == 0) {
 
               return f;
 
            }
 
         }
 
      }
 
 
 
      return square::NONE;
 
   }
 
 
 
public:
 
 
 
   int see(int mv, int alpha, int beta, const board::Board & bd) {
 
 
 
      assert(alpha < beta);
 
 
 
      p_board = &bd;
 
 
 
      int f = from(mv);
 
      int t = to(mv);
 
 
 
      int pc = p_board->square(f);
 
      int sd = p_board->square_side(f);
 
 
 
      init(t, sd);
 
      int cap = move(f); // NOTE: assumes queen promotion
 
 
 
      if (pc == piece::PAWN && square::is_promotion(t)) { // adjust for under-promotion
 
         int delta = piece::QUEEN_VALUE - piece::value(prom(mv));
 
         cap   -= delta;
 
         p_val -= delta;
 
      }
 
 
 
      return cap - see_rec(cap - beta, cap - alpha);
 
   }
 
 
 
};
 
 
 
bool is_capture(int mv) {
 
   return cap(mv) != piece::NONE;
 
}
 
 
 
bool is_en_passant(int mv, const board::Board & bd) {
 
   return piece(mv) == piece::PAWN && to(mv) == bd.ep_sq();
 
}
 
 
 
bool is_recapture(int mv, const board::Board & bd) {
 
   return to(mv) == bd.recap() && is_win(mv, bd);
 
}
 
 
 
bool is_promotion(int mv) {
 
   return prom(mv) != piece::NONE;
 
}
 
 
 
bool is_queen_promotion(int mv) {
 
   return prom(mv) == piece::QUEEN;
 
}
 
 
 
bool is_under_promotion(int mv) {
 
   int pp = prom(mv);
 
   return pp != piece::NONE && pp != piece::QUEEN;
 
}
 
 
 
bool is_tactical(int mv) {
 
   return is_capture(mv) || is_promotion(mv);
 
}
 
 
 
bool is_pawn_push(int mv, const board::Board & bd) {
 
 
 
   if (is_tactical(mv)) {
 
      return false;
 
   }
 
 
 
   int f = from(mv);
 
   int t = to(mv);
 
 
 
   int pc = bd.square(f);
 
   int sd = bd.square_side(f);
 
 
 
   return pc == piece::PAWN && square::rank(t, sd) >= square::RANK_6 && pawn::is_passed(t, sd, bd) && !is_capture(mv);
 
}
 
 
 
bool is_castling(int mv) {
 
   return piece(mv) == piece::KING && std::abs(to(mv) - from(mv)) == square::CASTLING_DELTA;
 
}
 
 
 
bool is_conversion(int mv) {
 
   return is_capture(mv) || piece(mv) == piece::PAWN || is_castling(mv);
 
}
 
 
 
int make(int f, int t, int pp, const board::Board & bd) {
 
 
 
   int pc = bd.square(f);
 
   int cp = bd.square(t);
 
 
 
   if (pc == piece::PAWN && t == bd.ep_sq()) {
 
      cp = piece::PAWN;
 
   }
 
 
 
   if (pc == piece::PAWN && square::is_promotion(t) && pp == piece::NONE) { // not needed
 
      pp = piece::QUEEN;
 
   }
 
 
 
   return make(f, t, pc, cp, pp);
 
}
 
 
 
int from_string(const std::string & s, const board::Board & bd) {
 
 
 
   assert(s.length() >= 4);
 
 
 
   int f = square::from_string(s.substr(0, 2));
 
   int t = square::from_string(s.substr(2, 2));
 
   int pp = (s.length() > 4) ? piece::from_char(std::toupper(s[4])) : piece::NONE;
 
 
 
   return make(f, t, pp, bd);
 
}
 
 
 
int see(int mv, int alpha, int beta, const board::Board & bd) {
 
   SEE see;
 
   return see.see(mv, alpha, beta, bd);
 
}
 
 
 
int see_max(int mv) {
 
 
 
   assert(is_tactical(mv));
 
 
 
   int sc = piece::value(cap(mv));
 
 
 
   int pp = prom(mv);
 
   if (pp != piece::NONE) sc += piece::value(pp) - piece::PAWN_VALUE;
 
 
 
   return sc;
 
}
 
 
 
bool is_safe(int mv, const board::Board & bd) {
 
 
 
   int pc = piece(mv);
 
   int cp = cap(mv);
 
   int pp = prom(mv);
 
 
 
   if (false) {
 
   } else if (pc == piece::KING) {
 
      return true;
 
   } else if (piece::value(cp) >= piece::value(pc)) {
 
      return true;
 
   } else if (pp != piece::NONE && pp != piece::QUEEN) { // under-promotion
 
      return false;
 
   } else {
 
      return see(mv, -1, 0, bd) >= 0;
 
   }
 
}
 
 
 
bool is_win(int mv, const board::Board & bd) {
 
 
 
   assert(is_tactical(mv));
 
 
 
   int pc = piece(mv);
 
   int cp = cap(mv);
 
   int pp = prom(mv);
 
 
 
   if (false) {
 
   } else if (pc == piece::KING) {
 
      return true;
 
   } else if (piece::value(cp) > piece::value(pc)) {
 
      return true;
 
   } else if (pp != piece::NONE && pp != piece::QUEEN) { // under-promotion
 
      return false;
 
   } else {
 
      return see(mv, 0, +1, bd) > 0;
 
   }
 
}
 
 
 
bool is_legal_debug(int mv, board::Board & bd) {
 
 
 
   bd.move(mv);
 
   bool b = attack::is_legal(bd);
 
   bd.undo();
 
 
 
   return b;
 
}
 
 
 
bool is_legal(int mv, board::Board & bd, const attack::Attacks & attacks) {
 
 
 
   int sd = bd.turn();
 
 
 
   int f = from(mv);
 
   int t = to(mv);
 
 
 
   if (is_en_passant(mv, bd)) {
 
      return is_legal_debug(mv, bd);
 
   }
 
 
 
   if (piece(mv) == piece::KING) {
 
      return !attack::is_attacked(t, side::opposit(sd), bd);
 
   }
 
 
 
   if (!bit::is_set(attacks.pinned, f)) {
 
      return true;
 
   }
 
 
 
   if (bit::is_set(attack::ray(bd.king(sd), f), t)) {
 
      return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
bool is_check_debug(int mv, board::Board & bd) {
 
 
 
   bd.move(mv);
 
   bool b = attack::is_in_check(bd);
 
   bd.undo();
 
 
 
   return b;
 
}
 
 
 
bool is_check(int mv, board::Board & bd) {
 
 
 
   if (is_promotion(mv) || is_en_passant(mv, bd) || is_castling(mv)) {
 
      return is_check_debug(mv, bd);
 
   }
 
 
 
   int f = from(mv);
 
   int t = to(mv);
 
 
 
   int pc = (prom(mv) != piece::NONE) ? prom(mv) : piece(mv);
 
   int sd = bd.square_side(f);
 
 
 
   int king = bd.king(side::opposit(sd));
 
 
 
   if (attack::attack(pc, sd, t, king, bd)) {
 
      return true;
 
   }
 
 
 
   if (attack::attack_behind(king, f, sd, bd) && !bit::is_set(attack::ray(king, f), t)) {
 
      return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
}
 
 
 
namespace sort {
 
 
 
class Killer {
 
 
 
private:
 
 
 
   static const int PLY = 100;
 
 
 
   struct List {
 
      int k0;
 
      int k1;
 
   };
 
 
 
   List p_killer[PLY];
 
 
 
public:
 
 
 
   void clear() {
 
      for (int ply = 0; ply < PLY; ply++) {
 
         p_killer[ply].k0 = move::NONE;
 
         p_killer[ply].k1 = move::NONE;
 
      }
 
   }
 
 
 
   void add(int mv, int ply) {
 
      if (p_killer[ply].k0 != mv) {
 
         p_killer[ply].k1 = p_killer[ply].k0;
 
         p_killer[ply].k0 = mv;
 
      }
 
   }
 
 
 
   int killer_0(int ply) const {
 
      return p_killer[ply].k0;
 
   }
 
 
 
   int killer_1(int ply) const {
 
      return p_killer[ply].k1;
 
   }
 
 
 
};
 
 
 
class History {
 
 
 
private:
 
 
 
   static const int PROB_BIT   = 11;
 
   static const int PROB_ONE   = 1 << PROB_BIT;
 
   static const int PROB_HALF  = 1 << (PROB_BIT - 1);
 
   static const int PROB_SHIFT = 5;
 
 
 
   int p_prob[piece::SIDE_SIZE * square::SIZE];
 
 
 
   static int index(int mv, const board::Board & bd) {
 
 
 
      assert(!move::is_tactical(mv));
 
 
 
      int sd = bd.square_side(move::from(mv));
 
      int p12 = piece::make(move::piece(mv), sd);
 
 
 
      int idx = p12 * square::SIZE + move::to(mv);
 
      assert(idx < piece::SIDE_SIZE * square::SIZE);
 
 
 
      return idx;
 
   }
 
 
 
   void update_good(int mv, const board::Board & bd) {
 
      if (!move::is_tactical(mv)) {
 
         int idx = index(mv, bd);
 
         p_prob[idx] += (PROB_ONE - p_prob[idx]) >> PROB_SHIFT;
 
      }
 
   }
 
 
 
   void update_bad(int mv, const board::Board & bd) {
 
      if (!move::is_tactical(mv)) {
 
         int idx = index(mv, bd);
 
         p_prob[idx] -= p_prob[idx] >> PROB_SHIFT;
 
      }
 
   }
 
 
 
public:
 
 
 
   void clear() {
 
      for (int idx = 0; idx < piece::SIDE_SIZE * square::SIZE; idx++) {
 
         p_prob[idx] = PROB_HALF;
 
      }
 
   }
 
 
 
   void add(int bm, const gen::List & searched, const board::Board & bd) {
 
 
 
      assert(bm != move::NONE);
 
 
 
      update_good(bm, bd);
 
 
 
      for (int pos = 0; pos < searched.size(); pos++) {
 
         int mv = searched.move(pos);
 
         if (mv != bm) update_bad(mv, bd);
 
      }
 
   }
 
 
 
   int score(int mv, const board::Board & bd) const {
 
      int idx = index(mv, bd);
 
      return p_prob[idx];
 
   }
 
 
 
};
 
 
 
int capture_score_debug(int pc, int cp) {
 
   int sc = piece::score(cp) * 6 + (5 - piece::score(pc));
 
   assert(sc >= 0 && sc < 36);
 
   return sc;
 
}
 
 
 
int promotion_score_debug(int pp) {
 
   switch (pp) {
 
   case piece::QUEEN:
 
      return 3;
 
   case piece::KNIGHT:
 
      return 2;
 
   case piece::ROOK:
 
      return 1;
 
   case piece::BISHOP:
 
      return 0;
 
   default:
 
      assert(false);
 
      return 0;
 
   }
 
}
 
 
 
int tactical_score_debug(int pc, int cp, int pp) {
 
 
 
   int sc;
 
 
 
   if (cp != piece::NONE) {
 
      sc = capture_score_debug(pc, cp) + 4;
 
   } else {
 
      sc = promotion_score_debug(pp);
 
   }
 
 
 
   assert(sc >= 0 && sc < 40);
 
   return sc;
 
}
 
 
 
int capture_score(int mv) {
 
   assert(move::is_capture(mv));
 
   return capture_score_debug(move::piece(mv), move::cap(mv));
 
}
 
 
 
int promotion_score(int mv) {
 
   assert(move::is_promotion(mv) && !move::is_capture(mv));
 
   return promotion_score_debug(move::prom(mv));
 
}
 
 
 
int tactical_score(int mv) {
 
   assert(move::is_tactical(mv));
 
   return tactical_score_debug(move::piece(mv), move::cap(mv), move::prom(mv));
 
}
 
 
 
int evasion_score(int mv, int trans_move) {
 
 
 
   int sc;
 
 
 
   if (false) {
 
   } else if (mv == trans_move) {
 
      sc = move::SCORE_MASK;
 
   } else if (move::is_tactical(mv)) {
 
      sc = tactical_score(mv) + 1;
 
      assert (sc >= 1 && sc < 41);
 
   } else {
 
      sc = 0;
 
   }
 
 
 
   return sc;
 
}
 
 
 
void sort_tacticals(gen::List & ml) {
 
 
 
   for (int pos = 0; pos < ml.size(); pos++) {
 
      int mv = ml.move(pos);
 
      int sc = tactical_score(mv);
 
      ml.set_score(pos, sc);
 
   }
 
 
 
   ml.sort();
 
}
 
 
 
void sort_history(gen::List & ml, const board::Board & bd, const History & history) {
 
 
 
   for (int pos = 0; pos < ml.size(); pos++) {
 
      int mv = ml.move(pos);
 
      int sc = history.score(mv, bd);
 
      ml.set_score(pos, sc);
 
   }
 
 
 
   ml.sort();
 
}
 
 
 
void sort_evasions(gen::List & ml, int trans_move) {
 
 
 
   for (int pos = 0; pos < ml.size(); pos++) {
 
      int mv = ml.move(pos);
 
      int sc = evasion_score(mv, trans_move);
 
      ml.set_score(pos, sc);
 
   }
 
 
 
   ml.sort();
 
}
 
 
 
}
 
 
 
namespace gen_sort {
 
 
 
// HACK: outside of List class because of C++ "static const" limitations :(
 
 
 
enum Inst {
 
   GEN_EVASION,
 
   GEN_TRANS,
 
   GEN_TACTICAL,
 
   GEN_KILLER,
 
   GEN_CHECK,
 
   GEN_PAWN,
 
   GEN_QUIET,
 
   GEN_BAD,
 
   GEN_END,
 
   POST_MOVE,
 
   POST_MOVE_SEE,
 
   POST_KILLER,
 
   POST_KILLER_SEE,
 
   POST_BAD,
 
};
 
 
 
const Inst Prog_Main[]    = { GEN_TRANS, POST_KILLER, GEN_TACTICAL, POST_MOVE_SEE, GEN_KILLER, POST_KILLER_SEE, GEN_QUIET, POST_MOVE_SEE, GEN_BAD, POST_BAD, GEN_END };
 
const Inst Prog_QS_Root[] = { GEN_TRANS, POST_KILLER, GEN_TACTICAL, POST_MOVE, GEN_CHECK, POST_KILLER, GEN_PAWN, POST_MOVE, GEN_END };
 
const Inst Prog_QS[]      = { GEN_TRANS, POST_KILLER, GEN_TACTICAL, POST_MOVE, GEN_END };
 
const Inst Prog_Evasion[] = { GEN_EVASION, POST_MOVE_SEE, GEN_BAD, POST_BAD, GEN_END };
 
 
 
class List {
 
 
 
private:
 
 
 
   board::Board * p_board;
 
   const attack::Attacks * p_attacks;
 
   const sort::Killer * p_killer;
 
   const sort::History * p_hist;
 
   int p_trans_move;
 
 
 
   const Inst * p_ip;
 
   Inst p_gen;
 
   Inst p_post;
 
 
 
   gen::List p_todo;
 
   gen::List p_done;
 
   gen::List p_bad;
 
 
 
   int p_pos;
 
   bool p_candidate;
 
 
 
   bool gen() {
 
 
 
      p_todo.clear();
 
      p_pos = 0;
 
 
 
      switch (p_gen) { {
 
 
 
      } case GEN_EVASION: {
 
 
 
         gen::add_evasions(p_todo, p_board->turn(), *p_board, *p_attacks);
 
         sort::sort_evasions(p_todo, p_trans_move);
 
         break;
 
 
 
      } case GEN_TRANS: {
 
 
 
         int mv = p_trans_move;
 
 
 
         if (mv != move::NONE && gen::is_move(mv, *p_board)) {
 
            p_todo.add(mv);
 
         }
 
 
 
         p_candidate = true;
 
 
 
         break;
 
 
 
      } case GEN_TACTICAL: {
 
 
 
         gen::add_captures(p_todo, p_board->turn(), *p_board);
 
         gen::add_promotions(p_todo, p_board->turn(), *p_board);
 
         sort::sort_tacticals(p_todo);
 
 
 
         p_candidate = true;
 
 
 
         break;
 
 
 
      } case GEN_KILLER: {
 
 
 
         int k0 = p_killer->killer_0(p_board->ply());
 
 
 
         if (k0 != move::NONE && gen::is_quiet_move(k0, *p_board)) {
 
            p_todo.add(k0);
 
         }
 
 
 
         int k1 = p_killer->killer_1(p_board->ply());
 
 
 
         if (k1 != move::NONE && gen::is_quiet_move(k1, *p_board)) {
 
            p_todo.add(k1);
 
         }
 
 
 
         p_candidate = true;
 
 
 
         break;
 
 
 
      } case GEN_CHECK: {
 
 
 
         gen::add_checks(p_todo, p_board->turn(), *p_board);
 
 
 
         p_candidate = true; // not needed yet
 
 
 
         break;
 
 
 
      } case GEN_PAWN: {
 
 
 
         gen::add_castling(p_todo, p_board->turn(), *p_board);
 
         gen::add_pawn_pushes(p_todo, p_board->turn(), *p_board);
 
 
 
         p_candidate = true; // not needed yet
 
 
 
         break;
 
 
 
      } case GEN_QUIET: {
 
 
 
         gen::add_quiets(p_todo, p_board->turn(), *p_board);
 
         sort::sort_history(p_todo, *p_board, *p_hist);
 
 
 
         p_candidate = false;
 
 
 
         break;
 
 
 
      } case GEN_BAD: {
 
 
 
         p_todo = p_bad;
 
 
 
         p_candidate = false;
 
 
 
         break;
 
 
 
      } case GEN_END: {
 
 
 
         return false;
 
 
 
      } default: {
 
 
 
         assert(false);
 
 
 
      } }
 
 
 
      return true;
 
   }
 
 
 
   bool post(int mv) {
 
 
 
      assert(mv != move::NONE);
 
 
 
      switch (p_post) { {
 
 
 
      } case POST_MOVE: {
 
 
 
         if (p_done.contain(mv)) {
 
            return false;
 
         }
 
 
 
         if (!move::is_legal(mv, *p_board, *p_attacks)) {
 
            return false;
 
         }
 
 
 
         break;
 
 
 
      } case POST_MOVE_SEE: {
 
 
 
         if (p_done.contain(mv)) {
 
            return false;
 
         }
 
 
 
         if (!move::is_legal(mv, *p_board, *p_attacks)) {
 
            return false;
 
         }
 
 
 
         if (!move::is_safe(mv, *p_board)) {
 
            p_bad.add(mv);
 
            return false;
 
         }
 
 
 
         break;
 
 
 
      } case POST_KILLER: {
 
 
 
         if (p_done.contain(mv)) {
 
            return false;
 
         }
 
 
 
         if (!move::is_legal(mv, *p_board, *p_attacks)) {
 
            return false;
 
         }
 
 
 
         p_done.add(mv);
 
 
 
         break;
 
 
 
      } case POST_KILLER_SEE: {
 
 
 
         if (p_done.contain(mv)) {
 
            return false;
 
         }
 
 
 
         if (!move::is_legal(mv, *p_board, *p_attacks)) {
 
            return false;
 
         }
 
 
 
         p_done.add(mv);
 
 
 
         if (!move::is_safe(mv, *p_board)) {
 
            p_bad.add(mv);
 
            return false;
 
         }
 
 
 
         break;
 
 
 
      } case POST_BAD: {
 
 
 
         assert(move::is_legal(mv, *p_board, *p_attacks));
 
 
 
         break;
 
 
 
      } default: {
 
 
 
         assert(false);
 
 
 
      } }
 
 
 
      return true;
 
   }
 
 
 
public:
 
 
 
   void init(int depth, board::Board & bd, const attack::Attacks & attacks, int trans_move, const sort::Killer & killer, const sort::History & history, bool use_fp = false) {
 
 
 
      p_board = &bd;
 
      p_attacks = &attacks;
 
      p_killer = &killer;
 
      p_hist = &history;
 
      p_trans_move = trans_move;
 
 
 
      if (false) {
 
      } else if (attacks.size != 0) { // in check
 
         p_ip = Prog_Evasion;
 
      } else if (depth < 0) {
 
         p_ip = Prog_QS;
 
      } else if (depth == 0) {
 
         p_ip = Prog_QS_Root;
 
      } else if (use_fp) {
 
         p_ip = Prog_QS_Root;
 
      } else {
 
         p_ip = Prog_Main;
 
      }
 
 
 
      p_todo.clear();
 
      p_done.clear();
 
      p_bad.clear();
 
 
 
      p_pos = 0;
 
      p_candidate = false;
 
   }
 
 
 
   int next() {
 
 
 
      while (true) {
 
 
 
         while (p_pos >= p_todo.size()) {
 
 
 
            p_gen  = *p_ip++;
 
            p_post = *p_ip++;
 
 
 
            if (!gen()) return move::NONE;
 
         }
 
 
 
         int mv = p_todo.move(p_pos++);
 
         if (post(mv)) return mv;
 
      }
 
   }
 
 
 
   bool is_candidate() const {
 
      return p_candidate;
 
   }
 
 
 
};
 
 
 
}
 
 
 
namespace material {
 
 
 
const int p_power[piece::SIZE] = { 0, 1, 1, 2, 4, 0, 0 };
 
const int p_score[piece::SIZE][stage::SIZE] = { { 85, 95 }, { 325, 325 }, { 325, 325 }, { 460, 540 }, { 975, 975 }, { 0, 0 }, { 0, 0 } };
 
 
 
int phase_weight[TOTAL_PHASE + 1];
 
 
 
int power(int pc) {
 
   assert(pc < piece::SIZE);
 
   return p_power[pc];
 
}
 
 
 
int score(int pc, int stage) {
 
   assert(pc < piece::SIZE);
 
   assert(stage < stage::SIZE);
 
   return p_score[pc][stage];
 
}
 
 
 
int force(int sd, const board::Board & bd) { // for draw eval
 
 
 
   int force = 0;
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::QUEEN; pc++) {
 
      force += bd.count(pc, sd) * power(pc);
 
   }
 
 
 
   return force;
 
}
 
 
 
bool lone_king(int sd, const board::Board & bd) {
 
 
 
   return bd.count(piece::KNIGHT, sd) == 0
 
       && bd.count(piece::BISHOP, sd) == 0
 
       && bd.count(piece::ROOK,   sd) == 0
 
       && bd.count(piece::QUEEN,  sd) == 0;
 
}
 
 
 
bool lone_bishop(int sd, const board::Board & bd) {
 
 
 
   return bd.count(piece::KNIGHT, sd) == 0
 
       && bd.count(piece::BISHOP, sd) == 1
 
       && bd.count(piece::ROOK,   sd) == 0
 
       && bd.count(piece::QUEEN,  sd) == 0;
 
}
 
 
 
bool lone_king_or_bishop(int sd, const board::Board & bd) {
 
 
 
   return bd.count(piece::KNIGHT, sd) == 0
 
       && bd.count(piece::BISHOP, sd) <= 1
 
       && bd.count(piece::ROOK,   sd) == 0
 
       && bd.count(piece::QUEEN,  sd) == 0;
 
}
 
 
 
bool lone_king_or_minor(int sd, const board::Board & bd) {
 
 
 
   return bd.count(piece::KNIGHT, sd) + bd.count(piece::BISHOP, sd) <= 1
 
       && bd.count(piece::ROOK,   sd) == 0
 
       && bd.count(piece::QUEEN,  sd) == 0;
 
}
 
 
 
bool two_knights(int sd, const board::Board & bd) {
 
 
 
   return bd.count(piece::KNIGHT, sd) == 2
 
       && bd.count(piece::BISHOP, sd) == 0
 
       && bd.count(piece::ROOK,   sd) == 0
 
       && bd.count(piece::QUEEN,  sd) == 0;
 
}
 
 
 
int interpolation(int mg, int eg, const board::Board & bd) {
 
 
 
   int phase = std::min(bd.phase(), TOTAL_PHASE);
 
   assert(phase >= 0 && phase <= TOTAL_PHASE);
 
 
 
   int weight = phase_weight[phase];
 
   return (mg * weight + eg * (256 - weight) + 128) >> 8;
 
}
 
 
 
void init() {
 
 
 
   for (int i = 0; i <= TOTAL_PHASE; i++) {
 
      double x = double(i) / double(TOTAL_PHASE / 2) - 1.0;
 
      double y = 1.0 / (1.0 + std::exp(-x * 5.0));
 
      phase_weight[i] = util::round(y * 256.0);
 
   }
 
}
 
 
 
}
 
 
 
namespace pst {
 
 
 
const int Knight_Line[8]  = { -4, -2,  0, +1, +1,  0, -2, -4 };
 
const int King_Line[8]    = { -3, -1,  0, +1, +1,  0, -1, -3 };
 
const int King_File[8]    = { +1, +2,  0, -2, -2,  0, +2, +1 };
 
const int King_Rank[8]    = { +1,  0, -2, -4, -6, -8,-10,-12 };
 
 
 
const int Advance_Rank[8] = { -3, -2, -1,  0, +1, +2, +1,  0 };
 
 
 
int p_score[piece::SIDE_SIZE][square::SIZE][stage::SIZE];
 
 
 
int score(int p12, int sq, int stage) {
 
   return p_score[p12][sq][stage];
 
}
 
 
 
void init() {
 
 
 
   for (int p12 = 0; p12 < piece::SIDE_SIZE; p12++) {
 
      for (int sq = 0; sq < square::SIZE; sq++) {
 
         p_score[p12][sq][stage::MG] = 0;
 
         p_score[p12][sq][stage::EG] = 0;
 
      }
 
   }
 
 
 
   for (int sq = 0; sq < square::SIZE; sq++) {
 
 
 
      int fl = square::file(sq);
 
      int rk = square::rank(sq);
 
 
 
      p_score[piece::WHITE_PAWN][sq][stage::MG] = 0;
 
      p_score[piece::WHITE_PAWN][sq][stage::EG] = 0;
 
 
 
      p_score[piece::WHITE_KNIGHT][sq][stage::MG] = (Knight_Line[fl] + Knight_Line[rk] + Advance_Rank[rk]) * 4;
 
      p_score[piece::WHITE_KNIGHT][sq][stage::EG] = (Knight_Line[fl] + Knight_Line[rk] + Advance_Rank[rk]) * 4;
 
 
 
      p_score[piece::WHITE_BISHOP][sq][stage::MG] = (King_Line[fl] + King_Line[rk]) * 2;
 
      p_score[piece::WHITE_BISHOP][sq][stage::EG] = (King_Line[fl] + King_Line[rk]) * 2;
 
 
 
      p_score[piece::WHITE_ROOK][sq][stage::MG] = King_Line[fl] * 5;
 
      p_score[piece::WHITE_ROOK][sq][stage::EG] = 0;
 
 
 
      p_score[piece::WHITE_QUEEN][sq][stage::MG] = (King_Line[fl] + King_Line[rk]) * 1;
 
      p_score[piece::WHITE_QUEEN][sq][stage::EG] = (King_Line[fl] + King_Line[rk]) * 1;
 
 
 
      p_score[piece::WHITE_KING][sq][stage::MG] = (King_File[fl] + King_Rank[rk]) * 8;
 
      p_score[piece::WHITE_KING][sq][stage::EG] = (King_Line[fl] + King_Line[rk] + Advance_Rank[rk]) * 8;
 
   }
 
 
 
   for (int pc = piece::PAWN; pc <= piece::KING; pc++) {
 
 
 
      int wp = piece::make(pc, side::WHITE);
 
      int bp = piece::make(pc, side::BLACK);
 
 
 
      for (int bs = 0; bs < square::SIZE; bs++) {
 
 
 
         int ws = square::opposit_rank(bs);
 
 
 
         p_score[bp][bs][stage::MG] = p_score[wp][ws][stage::MG];
 
         p_score[bp][bs][stage::EG] = p_score[wp][ws][stage::EG];
 
      }
 
   }
 
}
 
 
 
}
 
 
 
namespace pawn {
 
 
 
struct Info { // 80 bytes; TODO: merge some bitboards and/or file info?
 
   int8 open[square::FILE_SIZE][side::SIZE];
 
   uint8 shelter[square::FILE_SIZE][side::SIZE];
 
   bit_t passed;
 
   bit_t target[side::SIZE];
 
   bit_t safe;
 
   uint32 lock;
 
   int16 mg;
 
   int16 eg;
 
   int8 left_file;
 
   int8 right_file;
 
};
 
 
 
void clear_info (Info & info);
 
void comp_info  (Info & info, const board::Board & bd);
 
 
 
class Table {
 
 
 
private:
 
 
 
   static const int BITS = 12;
 
   static const int SIZE = 1 << BITS;
 
   static const int MASK = SIZE - 1;
 
 
 
   Info p_table[SIZE];
 
 
 
public:
 
 
 
   void clear() {
 
 
 
      Info info;
 
      clear_info(info);
 
 
 
      for (int index = 0; index < SIZE; index++) {
 
         p_table[index] = info;
 
      }
 
   }
 
 
 
   void clear_fast() {
 
      for (int index = 0; index < SIZE; index++) {
 
         p_table[index].lock = 1; // board w/o pawns has key 0!
 
      }
 
   }
 
 
 
   const Info & info(const board::Board & bd) {
 
 
 
      hash_t key = bd.pawn_key();
 
 
 
      int    index = hash::index(key) & MASK;
 
      uint32 lock  = hash::lock(key);
 
 
 
      Info & entry = p_table[index];
 
 
 
      if (entry.lock != lock) {
 
         entry.lock = lock;
 
         comp_info(entry, bd);
 
      }
 
 
 
      return entry;
 
   }
 
 
 
};
 
 
 
bit_t duo[square::SIZE];
 
 
 
void clear_info(Info & info) {
 
 
 
   info.passed = 0;
 
   info.safe = 0;
 
   info.lock = 1; // board w/o pawns has key 0!
 
   info.mg = 0;
 
   info.eg = 0;
 
   info.left_file  = square::FILE_A;
 
   info.right_file = square::FILE_H;
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      info.target[sd] = 0;
 
   }
 
 
 
   for (int fl = 0; fl < square::FILE_SIZE; fl++) {
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         info.open[fl][sd] = 0;
 
         info.shelter[fl][sd] = 0;
 
      }
 
   }
 
}
 
 
 
bool is_empty(int sq, const board::Board & bd) {
 
   return bd.square(sq) != piece::PAWN;
 
}
 
 
 
bool is_attacked(int sq, int sd, const board::Board & bd) {
 
   return (bd.piece(piece::PAWN, sd) & attack::pawn_attacks_to(sd, sq)) != 0;
 
}
 
 
 
bool is_controlled(int sq, int sd, const board::Board & bd) {
 
 
 
   bit_t attackers = bd.piece(piece::PAWN, sd) & attack::pawn_attacks_to(sd, sq);
 
   bit_t defenders = bd.piece(piece::PAWN, side::opposit(sd)) & attack::pawn_attacks_to(side::opposit(sd), sq);
 
 
 
   return bit::count_loop(attackers) > bit::count_loop(defenders);
 
}
 
 
 
bool is_safe(int sq, int sd, const board::Board & bd) {
 
   return is_empty(sq, bd) && !is_controlled(sq, side::opposit(sd), bd);
 
}
 
 
 
bit_t potential_attacks(int sq, int sd, const board::Board & bd) {
 
 
 
   int inc = square::pawn_inc(sd);
 
 
 
   bit_t attacks = attack::pawn_attacks_from(sd, sq);
 
 
 
   for (sq += inc; !square::is_promotion(sq) && is_safe(sq, sd, bd); sq += inc) {
 
      attacks |= attack::pawn_attacks_from(sd, sq);
 
   }
 
 
 
   return attacks;
 
}
 
 
 
bool is_duo(int sq, int sd, const board::Board & bd) {
 
   return (bd.piece(piece::PAWN, sd) & duo[sq]) != 0;
 
}
 
 
 
bool is_isolated(int sq, int sd, const board::Board & bd) {
 
 
 
   int fl = square::file(sq);
 
   bit_t files = bit::files(fl) & ~bit::file(fl);
 
 
 
   return (bd.piece(piece::PAWN, sd) & files) == 0;
 
}
 
 
 
bool is_weak(int sq, int sd, const board::Board & bd) {
 
 
 
   int fl = square::file(sq);
 
   int rk = square::rank(sq, sd);
 
 
 
   uint64 pawns = bd.piece(piece::PAWN, sd);
 
   int inc = square::pawn_inc(sd);
 
 
 
   // already fine?
 
 
 
   if ((pawns & duo[sq]) != 0) {
 
      return false;
 
   }
 
 
 
   if (is_attacked(sq, sd, bd)) {
 
      return false;
 
   }
 
 
 
   // can advance next to other pawn in one move?
 
 
 
   int s1 = sq + inc;
 
   int s2 = s1 + inc;
 
 
 
   if ((pawns & duo[s1]) != 0 && is_safe(s1, sd, bd)) {
 
      return false;
 
   }
 
 
 
   if (rk == square::RANK_2 && (pawns & duo[s2]) != 0 && is_safe(s1, sd, bd) && is_safe(s2, sd, bd)) {
 
      return false;
 
   }
 
 
 
   // can be defended in one move?
 
 
 
   if (fl != square::FILE_A) {
 
 
 
      int s0 = sq + square::INC_LEFT;
 
      int s1 = s0 - inc;
 
      int s2 = s1 - inc;
 
      int s3 = s2 - inc;
 
 
 
      if (bd.square_is(s2, piece::PAWN, sd) && is_safe(s1, sd, bd)) {
 
         return false;
 
      }
 
 
 
      if (rk == square::RANK_5 && bd.square_is(s3, piece::PAWN, sd) && is_safe(s2, sd, bd) && is_safe(s1, sd, bd)) {
 
         return false;
 
      }
 
   }
 
 
 
   if (fl != square::FILE_H) {
 
 
 
      int s0 = sq + square::INC_RIGHT;
 
      int s1 = s0 - inc;
 
      int s2 = s1 - inc;
 
      int s3 = s2 - inc;
 
 
 
      if (bd.square_is(s2, piece::PAWN, sd) && is_safe(s1, sd, bd)) {
 
         return false;
 
      }
 
 
 
      if (rk == square::RANK_5 && bd.square_is(s3, piece::PAWN, sd) && is_safe(s2, sd, bd) && is_safe(s1, sd, bd)) {
 
         return false;
 
      }
 
   }
 
 
 
   return true;
 
}
 
 
 
bool is_doubled(int sq, int sd, const board::Board & bd) {
 
   int fl = square::file(sq);
 
   return (bd.piece(piece::PAWN, sd) & bit::file(fl) & bit::rear(sq, sd)) != 0;
 
}
 
 
 
bool is_blocked(int sq, int sd, const board::Board & bd) {
 
   return !is_safe(square::stop(sq, sd), sd, bd) && !is_attacked(sq, side::opposit(sd), bd);
 
}
 
 
 
int shelter_file(int fl, int sd, const board::Board & bd) {
 
 
 
   assert(fl >= 0 && fl < 8);
 
 
 
   if (false) {
 
   } else if (bd.square_is(square::make(fl, square::RANK_2, sd), piece::PAWN, sd)) {
 
      return 2;
 
   } else if (bd.square_is(square::make(fl, square::RANK_3, sd), piece::PAWN, sd)) {
 
      return 1;
 
   } else {
 
      return 0;
 
   }
 
}
 
 
 
int shelter_files(int fl, int sd, const board::Board & bd) {
 
 
 
   int fl_left  = (fl == square::FILE_A) ? fl + 1 : fl - 1;
 
   int fl_right = (fl == square::FILE_H) ? fl - 1 : fl + 1;
 
 
 
   int sc = shelter_file(fl, sd, bd) * 2 + shelter_file(fl_left, sd, bd) + shelter_file(fl_right, sd, bd);
 
   assert(sc >= 0 && sc <= 8);
 
 
 
   return sc;
 
}
 
 
 
void comp_info(Info & info, const board::Board & bd) {
 
 
 
   info.passed = 0;
 
   info.safe = 0;
 
 
 
   info.mg = 0;
 
   info.eg = 0;
 
 
 
   info.left_file  = square::FILE_H + 1;
 
   info.right_file = square::FILE_A - 1;
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      info.target[sd] = 0;
 
   }
 
 
 
   bit_t weak = 0;
 
   bit_t strong = 0;
 
   bit_t safe[side::SIZE] = { bit_t(~0), bit_t(~0) };
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
      int p12 = piece::make(piece::PAWN, sd);
 
 
 
      strong |= bd.piece(piece::PAWN, sd) & attack::pawn_attacks_from(sd, bd); // defended pawns
 
 
 
      {
 
         int n = bd.count(piece::PAWN, sd);
 
         info.mg += n * material::score(piece::PAWN, stage::MG);
 
         info.eg += n * material::score(piece::PAWN, stage::EG);
 
      }
 
 
 
      for (bit_t b = bd.piece(piece::PAWN, sd); b != 0; b = bit::rest(b)) {
 
 
 
         int sq = bit::first(b);
 
 
 
         int fl = square::file(sq);
 
         int rk = square::rank(sq, sd);
 
 
 
         if (fl < info.left_file)  info.left_file  = fl;
 
         if (fl > info.right_file) info.right_file = fl;
 
 
 
         info.mg += pst::score(p12, sq, stage::MG);
 
         info.eg += pst::score(p12, sq, stage::EG);
 
 
 
         if (is_isolated(sq, sd, bd)) {
 
 
 
            bit::set(weak, sq);
 
 
 
            info.mg -= 10;
 
            info.eg -= 20;
 
 
 
         } else if (is_weak(sq, sd, bd)) {
 
 
 
            bit::set(weak, sq);
 
 
 
            info.mg -= 5;
 
            info.eg -= 10;
 
         }
 
 
 
         if (is_doubled(sq, sd, bd)) {
 
            info.mg -= 5;
 
            info.eg -= 10;
 
         }
 
 
 
         if (is_passed(sq, sd, bd)) {
 
 
 
            bit::set(info.passed, sq);
 
 
 
            info.mg += 10;
 
            info.eg += 20;
 
 
 
            if (rk >= square::RANK_5) {
 
               int stop = square::stop(sq, sd);
 
               if (is_duo(sq, sd, bd) && rk <= square::RANK_6) stop += square::pawn_inc(sd); // stop one line "later" for duos
 
               bit::set(info.target[side::opposit(sd)], stop);
 
            }
 
         }
 
 
 
         safe[side::opposit(sd)] &= ~potential_attacks(sq, sd, bd);
 
      }
 
 
 
      for (int fl = 0; fl < square::FILE_SIZE; fl++) {
 
         info.shelter[fl][sd] = shelter_files(fl, sd, bd) * 4;
 
      }
 
 
 
      info.mg = -info.mg;
 
      info.eg = -info.eg;
 
   }
 
 
 
   weak &= ~strong; // defended doubled pawns are not weak
 
   assert((weak & strong) == 0);
 
 
 
   info.target[side::WHITE] |= bd.piece(piece::PAWN, side::BLACK) & weak;
 
   info.target[side::BLACK] |= bd.piece(piece::PAWN, side::WHITE) & weak;
 
 
 
   info.safe = (safe[side::WHITE] & bit::front(square::RANK_4))
 
             | (safe[side::BLACK] & bit::rear (square::RANK_5));
 
 
 
   if (info.left_file > info.right_file) { // no pawns
 
      info.left_file  = square::FILE_A;
 
      info.right_file = square::FILE_H;
 
   }
 
 
 
   assert(info.left_file <= info.right_file);
 
 
 
   // file "openness"
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
      for (int fl = 0; fl < square::FILE_SIZE; fl++) {
 
 
 
         bit_t file = bit::file(fl);
 
 
 
         int open;
 
 
 
         if (false) {
 
         } else if ((bd.piece(piece::PAWN, sd) & file) != 0) {
 
            open = 0;
 
         } else if ((bd.piece(piece::PAWN, side::opposit(sd)) & file) == 0) {
 
            open = 4;
 
         } else if ((strong & file) != 0) {
 
            open = 1;
 
         } else if ((weak & file) != 0) {
 
            open = 3;
 
         } else {
 
            open = 2;
 
         }
 
 
 
         info.open[fl][sd] = open * 5;
 
      }
 
   }
 
}
 
 
 
void init() {
 
 
 
   for (int sq = 0; sq < square::SIZE; sq++) {
 
 
 
      int fl = square::file(sq);
 
      int rk = square::rank(sq);
 
 
 
      passed_me[sq][side::WHITE] = bit::file(fl) & bit::front(rk);
 
      passed_me[sq][side::BLACK] = bit::file(fl) & bit::rear(rk);
 
 
 
      passed_opp[sq][side::WHITE] = bit::files(fl) & bit::front(rk);
 
      passed_opp[sq][side::BLACK] = bit::files(fl) & bit::rear(rk);
 
 
 
      bit_t b = 0;
 
      if (fl != square::FILE_A) bit::set(b, sq + square::INC_LEFT);
 
      if (fl != square::FILE_H) bit::set(b, sq + square::INC_RIGHT);
 
      duo[sq] = b;
 
   }
 
}
 
 
 
}
 
 
 
namespace eval {
 
 
 
int comp_eval (const board::Board & bd, pawn::Table & pawn_table);
 
 
 
struct Entry {
 
   uint32 lock;
 
   int eval;
 
};
 
 
 
class Table {
 
 
 
private:
 
 
 
   static const int BITS = 16;
 
   static const int SIZE = 1 << BITS;
 
   static const int MASK = SIZE - 1;
 
 
 
   Entry p_table[SIZE];
 
 
 
public:
 
 
 
   void clear() {
 
      for (int index = 0; index < SIZE; index++) {
 
         p_table[index].lock = 0;
 
         p_table[index].eval = 0;
 
      }
 
   }
 
 
 
   int eval(const board::Board & bd, pawn::Table & pawn_table) { // NOTE: score for white
 
 
 
      hash_t key = bd.eval_key();
 
 
 
      int    index = hash::index(key) & MASK;
 
      uint32 lock  = hash::lock(key);
 
 
 
      Entry & entry = p_table[index];
 
 
 
      if (entry.lock == lock) {
 
         return entry.eval;
 
      }
 
 
 
      int eval = comp_eval(bd, pawn_table);
 
 
 
      entry.lock = lock;
 
      entry.eval = eval;
 
 
 
      return eval;
 
   }
 
 
 
};
 
 
 
struct Attack_Info {
 
 
 
   bit_t piece_attacks[square::SIZE];
 
   bit_t all_attacks[side::SIZE];
 
   bit_t multiple_attacks[side::SIZE];
 
 
 
   bit_t ge_pieces[side::SIZE][piece::SIZE];
 
 
 
   bit_t lt_attacks[side::SIZE][piece::SIZE];
 
   bit_t le_attacks[side::SIZE][piece::SIZE];
 
 
 
   bit_t king_evasions[side::SIZE];
 
 
 
   bit_t pinned;
 
};
 
 
 
const int attack_weight[piece::SIZE]   = { 0, 4, 4, 2, 1, 4, 0 };
 
const int attacked_weight[piece::SIZE] = { 0, 1, 1, 2, 4, 8, 0 };
 
 
 
int mob_weight[32];
 
int dist_weight[8]; // for king-passer distance
 
 
 
bit_t small_centre, medium_centre, large_centre;
 
bit_t centre_0, centre_1;
 
 
 
bit_t side_area[side::SIZE];
 
bit_t king_area[side::SIZE][square::SIZE];
 
 
 
void comp_attacks(Attack_Info & ai, const board::Board & bd) {
 
 
 
   // prepare for adding defended opponent pieces
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
      bit_t b = 0;
 
 
 
      for (int pc = piece::KING; pc >= piece::KNIGHT; pc--) {
 
         b |= bd.piece(pc, sd);
 
         ai.ge_pieces[sd][pc] = b;
 
      }
 
 
 
      ai.ge_pieces[sd][piece::BISHOP] = ai.ge_pieces[sd][piece::KNIGHT]; // minors are equal
 
   }
 
 
 
   // pawn attacks
 
 
 
   {
 
      int pc = piece::PAWN;
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
         bit_t b = attack::pawn_attacks_from(sd, bd);
 
 
 
         ai.lt_attacks[sd][pc] = 0; // not needed
 
         ai.le_attacks[sd][pc] = b;
 
         ai.all_attacks[sd] = b;
 
      }
 
   }
 
 
 
   // piece attacks
 
 
 
   ai.multiple_attacks[side::WHITE] = 0;
 
   ai.multiple_attacks[side::BLACK] = 0;
 
 
 
   for (int pc = piece::KNIGHT; pc <= piece::KING; pc++) {
 
 
 
      int lower_piece = (pc == piece::BISHOP) ? piece::PAWN : pc - 1; // HACK: direct access
 
      assert(lower_piece >= piece::PAWN && lower_piece < pc);
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
         ai.lt_attacks[sd][pc] = ai.le_attacks[sd][lower_piece];
 
      }
 
 
 
      for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
         for (bit_t fs = bd.piece(pc, sd); fs != 0; fs = bit::rest(fs)) {
 
 
 
            int sq = bit::first(fs);
 
 
 
            bit_t ts = attack::piece_attacks_from(pc, sq, bd);
 
            ai.piece_attacks[sq] = ts;
 
 
 
            ai.multiple_attacks[sd] |= ts & ai.all_attacks[sd];
 
            ai.all_attacks[sd] |= ts;
 
         }
 
 
 
         ai.le_attacks[sd][pc] = ai.all_attacks[sd];
 
         assert((ai.le_attacks[sd][pc] & ai.lt_attacks[sd][pc]) == ai.lt_attacks[sd][pc]);
 
 
 
         if (pc == piece::BISHOP) { // minors are equal
 
            ai.le_attacks[sd][piece::KNIGHT] = ai.le_attacks[sd][piece::BISHOP];
 
         }
 
      }
 
   }
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      int king = bd.king(sd);
 
      bit_t ts = attack::pseudo_attacks_from(piece::KING, sd, king);
 
      ai.king_evasions[sd] = ts & ~bd.side(sd) & ~ai.all_attacks[side::opposit(sd)];
 
   }
 
 
 
   // pinned pieces
 
 
 
   ai.pinned = 0;
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      int sq = bd.king(sd);
 
      ai.pinned |= bd.side(sd) & attack::pinned_by(sq, side::opposit(sd), bd);
 
   }
 
}
 
 
 
int mul_shift(int a, int b, int c) {
 
   int bias = 1 << (c - 1);
 
   return (a * b + bias) >> c;
 
}
 
 
 
int passed_score(int sc, int rk) {
 
   static const int passed_weight[8] = { 0, 0, 0, 2, 6, 12, 20, 0 };
 
   return mul_shift(sc, passed_weight[rk], 4);
 
}
 
 
 
int mobility_score(int /* pc */, bit_t ts) {
 
   // assert(pc < piece::SIZE);
 
   int mob = bit::count(ts);
 
   return mul_shift(20, mob_weight[mob], 8);
 
}
 
 
 
int attack_mg_score(int pc, int sd, bit_t ts) {
 
 
 
   assert(pc < piece::SIZE);
 
 
 
   int c0 = bit::count(ts & centre_0);
 
   int c1 = bit::count(ts & centre_1);
 
   int sc = c1 * 2 + c0;
 
 
 
   sc += bit::count(ts & side_area[side::opposit(sd)]);
 
 
 
   return (sc - 4) * attack_weight[pc] / 2;
 
}
 
 
 
int attack_eg_score(int pc, int sd, bit_t ts, const pawn::Info & pi) {
 
   assert(pc < piece::SIZE);
 
   return bit::count(ts & pi.target[sd]) * attack_weight[pc] * 4;
 
}
 
 
 
int capture_score(int pc, int sd, bit_t ts, const board::Board & bd, const Attack_Info & ai) {
 
 
 
   assert(pc < piece::SIZE);
 
 
 
   int sc = 0;
 
 
 
   for (bit_t b = ts & bd.pieces(side::opposit(sd)); b != 0; b = bit::rest(b)) {
 
 
 
      int t = bit::first(b);
 
 
 
      int cp = bd.square(t);
 
      sc += attacked_weight[cp];
 
      if (bit::is_set(ai.pinned, t)) sc += attacked_weight[cp] * 2;
 
   }
 
 
 
   return attack_weight[pc] * sc * 4;
 
}
 
 
 
int shelter_score(int sq, int sd, const board::Board & bd, const pawn::Info & pi) {
 
 
 
   if (square::rank(sq, sd) > square::RANK_2) {
 
      return 0;
 
   }
 
 
 
   int s0 = pi.shelter[square::file(sq)][sd];
 
 
 
   int s1 = 0;
 
 
 
   for (int wg = 0; wg < wing::SIZE; wg++) {
 
 
 
      int index = castling::index(sd, wg);
 
 
 
      if (castling::flag(bd.flags(), index)) {
 
         int fl = wing::shelter_file[wg];
 
         s1 = std::max(s1, int(pi.shelter[fl][sd]));
 
      }
 
   }
 
 
 
   if (s1 > s0) {
 
      return (s0 + s1) / 2;
 
   } else {
 
      return s0;
 
   }
 
}
 
 
 
int king_score(int sc, int n) {
 
   int weight = 256 - (256 >> n);
 
   return mul_shift(sc, weight, 8);
 
}
 
 
 
int eval_outpost(int sq, int sd, const board::Board & bd, const pawn::Info & pi) {
 
 
 
   assert(square::rank(sq, sd) >= square::RANK_5);
 
 
 
   int xd = side::opposit(sd);
 
 
 
   int weight = 0;
 
 
 
   if (bit::is_set(pi.safe, sq)) { // safe square
 
      weight += 2;
 
   }
 
 
 
   if (bd.square_is(square::stop(sq, sd), piece::PAWN, xd)) { // shielded
 
      weight++;
 
   }
 
 
 
   if (pawn::is_attacked(sq, sd, bd)) { // defended
 
      weight++;
 
   }
 
 
 
   return weight - 2;
 
}
 
 
 
bool passer_is_unstoppable(int sq, int sd, const board::Board & bd) {
 
 
 
   if (!material::lone_king(side::opposit(sd), bd)) return false;
 
 
 
   int fl = square::file(sq);
 
   bit_t front = bit::file(fl) & bit::front(sq, sd);
 
 
 
   if ((bd.all() & front) != 0) { // path not free
 
      return false;
 
   }
 
 
 
   if (pawn::square_distance(bd.king(side::opposit(sd)), sq, sd) >= 2) { // opponent king outside square
 
      return true;
 
   }
 
 
 
   if ((front & ~attack::pseudo_attacks_from(piece::KING, sd, bd.king(sd))) == 0) { // king controls promotion path
 
      return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
int eval_passed(int sq, int sd, const board::Board & bd, const Attack_Info & ai) {
 
 
 
   int fl = square::file(sq);
 
   int xd = side::opposit(sd);
 
 
 
   int weight = 4;
 
 
 
   // blocker
 
 
 
   if (bd.square(square::stop(sq, sd)) != piece::NONE) {
 
      weight--;
 
   }
 
 
 
   // free path
 
 
 
   bit_t front = bit::file(fl) & bit::front(sq, sd);
 
   bit_t rear  = bit::file(fl) & bit::rear (sq, sd);
 
 
 
   if ((bd.all() & front) == 0) {
 
 
 
      bool major_behind = false;
 
      bit_t majors = bd.piece(piece::ROOK, xd) | bd.piece(piece::QUEEN, xd);
 
 
 
      for (bit_t b = majors & rear; b != 0; b = bit::rest(b)) {
 
 
 
         int f = bit::first(b);
 
 
 
         if (attack::line_is_empty(f, sq, bd)) {
 
            major_behind = true;
 
         }
 
      }
 
 
 
      if (!major_behind && (ai.all_attacks[xd] & front) == 0) {
 
         weight += 2;
 
      }
 
   }
 
 
 
   return weight;
 
}
 
 
 
int eval_pawn_cap(int sd, const board::Board & bd, const Attack_Info & ai) {
 
 
 
   bit_t ts = attack::pawn_attacks_from(sd, bd);
 
 
 
   int sc = 0;
 
 
 
   for (bit_t b = ts & bd.pieces(side::opposit(sd)); b != 0; b = bit::rest(b)) {
 
 
 
      int t = bit::first(b);
 
 
 
      int cp = bd.square(t);
 
      if (cp == piece::KING) continue;
 
 
 
      sc += piece::value(cp) - 50;
 
      if (bit::is_set(ai.pinned, t)) sc += (piece::value(cp) - 50) * 2;
 
   }
 
 
 
   return sc / 10;
 
}
 
 
 
int eval_pattern(const board::Board & bd) {
 
 
 
   int eval = 0;
 
 
 
   // fianchetto
 
 
 
   if (bd.square_is(square::B2, piece::BISHOP, side::WHITE)
 
    && bd.square_is(square::B3, piece::PAWN,   side::WHITE)
 
    && bd.square_is(square::C2, piece::PAWN,   side::WHITE)) {
 
      eval += 20;
 
   }
 
 
 
   if (bd.square_is(square::G2, piece::BISHOP, side::WHITE)
 
    && bd.square_is(square::G3, piece::PAWN,   side::WHITE)
 
    && bd.square_is(square::F2, piece::PAWN,   side::WHITE)) {
 
      eval += 20;
 
   }
 
 
 
   if (bd.square_is(square::B7, piece::BISHOP, side::BLACK)
 
    && bd.square_is(square::B6, piece::PAWN,   side::BLACK)
 
    && bd.square_is(square::C7, piece::PAWN,   side::BLACK)) {
 
      eval -= 20;
 
   }
 
 
 
   if (bd.square_is(square::G7, piece::BISHOP, side::BLACK)
 
    && bd.square_is(square::G6, piece::PAWN,   side::BLACK)
 
    && bd.square_is(square::F7, piece::PAWN,   side::BLACK)) {
 
      eval -= 20;
 
   }
 
 
 
   return eval;
 
}
 
 
 
bool has_minor(int sd, const board::Board & bd) {
 
   return bd.count(piece::KNIGHT, sd) + bd.count(piece::BISHOP, sd) != 0;
 
}
 
 
 
int draw_mul(int sd, const board::Board & bd, const pawn::Info & pi) {
 
 
 
   int xd = side::opposit(sd);
 
 
 
   int pawn[side::SIZE];
 
   pawn[side::WHITE] = bd.count(piece::PAWN, side::WHITE);
 
   pawn[side::BLACK] = bd.count(piece::PAWN, side::BLACK);
 
 
 
   int force = material::force(sd, bd) - material::force(xd, bd);
 
 
 
   // rook-file pawns
 
 
 
   if (material::lone_king_or_bishop(sd, bd) && pawn[sd] != 0) {
 
 
 
      bit_t b = bd.piece(piece::BISHOP, sd);
 
 
 
      if ((bd.piece(piece::PAWN, sd) & ~bit::file(square::FILE_A)) == 0
 
       && (bd.piece(piece::PAWN, xd) &  bit::file(square::FILE_B)) == 0) {
 
 
 
         int prom = (sd == side::WHITE) ? square::A8 : square::A1;
 
 
 
         if (square::distance(bd.king(xd), prom) <= 1) {
 
            if (b == 0 || !square::same_colour(bit::first(b), prom)) {
 
               return 1;
 
            }
 
         }
 
      }
 
 
 
      if ((bd.piece(piece::PAWN, sd) & ~bit::file(square::FILE_H)) == 0
 
       && (bd.piece(piece::PAWN, xd) &  bit::file(square::FILE_G)) == 0) {
 
 
 
         int prom = (sd == side::WHITE) ? square::H8 : square::H1;
 
 
 
         if (square::distance(bd.king(xd), prom) <= 1) {
 
            if (b == 0 || !square::same_colour(bit::first(b), prom)) {
 
               return 1;
 
            }
 
         }
 
      }
 
   }
 
 
 
   if (pawn[sd] == 0 && material::lone_king_or_minor(sd, bd)) {
 
      return 1;
 
   }
 
 
 
   if (pawn[sd] == 0 && material::two_knights(sd, bd)) {
 
      return 2;
 
   }
 
 
 
   if (pawn[sd] == 0 && force <= 1) {
 
      return 2;
 
   }
 
 
 
   if (pawn[sd] == 1 && force == 0 && has_minor(xd, bd)) {
 
      return 4;
 
   }
 
 
 
   if (pawn[sd] == 1 && force == 0) {
 
 
 
      int king = bd.king(xd);
 
      int pawn = bit::first(bd.piece(piece::PAWN, sd));
 
      int stop = square::stop(pawn, sd);
 
 
 
      if (king == stop || (square::rank(pawn, sd) <= square::RANK_6 && king == square::stop(stop, sd))) {
 
         return 4;
 
      }
 
   }
 
 
 
   if (pawn[sd] == 2 && pawn[xd] >= 1 && force == 0 && has_minor(xd, bd) && (bd.piece(piece::PAWN, sd) & pi.passed) == 0) {
 
      return 8;
 
   }
 
 
 
   if (material::lone_bishop(side::WHITE, bd) && material::lone_bishop(side::BLACK, bd) && std::abs(pawn[side::WHITE] - pawn[side::BLACK]) <= 2) { // opposit-colour bishops
 
 
 
      int wb = bit::first(bd.piece(piece::BISHOP, side::WHITE));
 
      int bb = bit::first(bd.piece(piece::BISHOP, side::BLACK));
 
 
 
      if (!square::same_colour(wb, bb)) {
 
         return 8;
 
      }
 
   }
 
 
 
   return 16;
 
}
 
 
 
int my_distance(int f, int t, int weight) {
 
   int dist = square::distance(f, t);
 
   return mul_shift(dist_weight[dist], weight, 8);
 
}
 
 
 
int check_number(int pc, int sd, bit_t ts, int king, const board::Board & bd) {
 
 
 
   assert(pc != piece::KING);
 
 
 
   int xd = side::opposit(sd);
 
   bit_t checks = ts & ~bd.side(sd) & attack::pseudo_attacks_to(pc, sd, king);
 
 
 
   if (!piece::is_slider(pc)) {
 
      return bit::count_loop(checks);
 
   }
 
 
 
   int n = 0;
 
 
 
   bit_t b = checks & attack::pseudo_attacks_to(piece::KING, xd, king); // contact checks
 
   n += bit::count_loop(b) * 2;
 
   checks &= ~b;
 
 
 
   for (bit_t b = checks; b != 0; b = bit::rest(b)) {
 
 
 
      int t = bit::first(b);
 
 
 
      if (attack::line_is_empty(t, king, bd)) {
 
         n++;
 
      }
 
   }
 
 
 
   return n;
 
}
 
 
 
int comp_eval(const board::Board & bd, pawn::Table & pawn_table) { // NOTE: score for white
 
 
 
   Attack_Info ai;
 
   comp_attacks(ai, bd);
 
 
 
   const pawn::Info & pi = pawn_table.info(bd);
 
 
 
   int eval = 0;
 
   int mg = 0;
 
   int eg = 0;
 
 
 
   int shelter[side::SIZE];
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
      shelter[sd] = shelter_score(bd.king(sd), sd, bd, pi);
 
   }
 
 
 
   for (int sd = 0; sd < side::SIZE; sd++) {
 
 
 
      int xd = side::opposit(sd);
 
 
 
      int my_king = bd.king(sd);
 
      int op_king = bd.king(xd);
 
 
 
      bit_t target = ~(bd.piece(piece::PAWN, sd) | attack::pawn_attacks_from(xd, bd));
 
 
 
      int king_n = 0;
 
      int king_power = 0;
 
 
 
      // pawns
 
 
 
      {
 
         bit_t fs = bd.piece(piece::PAWN, sd);
 
 
 
         bit_t front = (sd == side::WHITE) ? bit::front(square::RANK_3) : bit::rear(square::RANK_6);
 
 
 
         for (bit_t b = fs & pi.passed & front; b != 0; b = bit::rest(b)) {
 
 
 
            int sq = bit::first(b);
 
 
 
            int rk = square::rank(sq, sd);
 
 
 
            if (passer_is_unstoppable(sq, sd, bd)) {
 
 
 
               int weight = std::max(rk - square::RANK_3, 0);
 
               assert(weight >= 0 and weight < 5);
 
 
 
               eg += (piece::QUEEN_VALUE - piece::PAWN_VALUE) * weight / 5;
 
 
 
            } else {
 
 
 
               int sc = eval_passed(sq, sd, bd, ai);
 
 
 
               int sc_mg = sc * 20;
 
               int sc_eg = sc * 25;
 
 
 
               int stop = square::stop(sq, sd);
 
               sc_eg -= my_distance(my_king, stop, 10);
 
               sc_eg += my_distance(op_king, stop, 20);
 
 
 
               mg += passed_score(sc_mg, rk);
 
               eg += passed_score(sc_eg, rk);
 
            }
 
         }
 
 
 
         eval += bit::count(attack::pawn_moves_from(sd, bd) & bd.empty()) * 4 - bd.count(piece::PAWN, sd) * 2;
 
 
 
         eval += eval_pawn_cap(sd, bd, ai);
 
      }
 
 
 
      // pieces
 
 
 
      for (int pc = piece::KNIGHT; pc <= piece::KING; pc++) {
 
 
 
         int p12 = piece::make(pc, sd); // for PST
 
 
 
         {
 
            int n = bd.count(pc, sd);
 
            mg += n * material::score(pc, stage::MG);
 
            eg += n * material::score(pc, stage::EG);
 
         }
 
 
 
         for (bit_t b = bd.piece(pc, sd); b != 0; b = bit::rest(b)) {
 
 
 
            int sq = bit::first(b);
 
 
 
            int fl = square::file(sq);
 
            int rk = square::rank(sq, sd);
 
 
 
            // compute safe attacks
 
 
 
            bit_t ts_all = ai.piece_attacks[sq];
 
            bit_t ts_pawn_safe = ts_all & target;
 
 
 
            bit_t safe = ~ai.all_attacks[xd] | ai.multiple_attacks[sd];
 
 
 
            if (true && piece::is_slider(pc)) { // battery support
 
 
 
               bit_t bishops = bd.piece(piece::BISHOP, sd) | bd.piece(piece::QUEEN, sd);
 
               bit_t rooks   = bd.piece(piece::ROOK,   sd) | bd.piece(piece::QUEEN, sd);
 
 
 
               bit_t support = 0;
 
               support |= bishops & attack::pseudo_attacks_to(piece::BISHOP, sd, sq);
 
               support |= rooks   & attack::pseudo_attacks_to(piece::ROOK,   sd, sq);
 
 
 
               for (bit_t b = ts_all & support; b != 0; b = bit::rest(b)) {
 
                  int f = bit::first(b);
 
                  assert(attack::line_is_empty(f, sq, bd));
 
                  safe |= attack::Behind[f][sq];
 
               }
 
            }
 
 
 
            bit_t ts_safe = ts_pawn_safe & ~ai.lt_attacks[xd][pc] & safe;
 
 
 
            mg += pst::score(p12, sq, stage::MG);
 
            eg += pst::score(p12, sq, stage::EG);
 
 
 
            if (pc == piece::KING) {
 
               eg += mobility_score(pc, ts_safe);
 
            } else {
 
               eval += mobility_score(pc, ts_safe);
 
            }
 
 
 
            if (pc != piece::KING) {
 
               mg += attack_mg_score(pc, sd, ts_pawn_safe);
 
            }
 
 
 
            eg += attack_eg_score(pc, sd, ts_pawn_safe, pi);
 
 
 
            eval += capture_score(pc, sd, ts_all & (ai.ge_pieces[xd][pc] | target), bd, ai);
 
 
 
            if (pc != piece::KING) {
 
               eval += check_number(pc, sd, ts_safe, op_king, bd) * material::power(pc) * 6;
 
            }
 
 
 
            if (pc != piece::KING && (ts_safe & king_area[xd][op_king]) != 0) { // king attack
 
               king_n++;
 
               king_power += material::power(pc);
 
            }
 
 
 
            if (piece::is_minor(pc) && rk >= square::RANK_5 && rk <= square::RANK_6 && fl >= square::FILE_C && fl <= square::FILE_F) { // outpost
 
               eval += eval_outpost(sq, sd, bd, pi) * 5;
 
            }
 
 
 
            if (piece::is_minor(pc) && rk >= square::RANK_5 && !bit::is_set(ai.all_attacks[sd], sq)) { // loose minor
 
               mg -= 10;
 
            }
 
 
 
            if (piece::is_minor(pc) && rk >= square::RANK_3 && rk <= square::RANK_4 && bd.square_is(square::stop(sq, sd), piece::PAWN, sd)) { // shielded minor
 
               mg += 10;
 
            }
 
 
 
            if (pc == piece::ROOK) { // open file
 
 
 
               int sc = pi.open[fl][sd];
 
 
 
               bit_t minors = bd.piece(piece::KNIGHT, xd) | bd.piece(piece::BISHOP, xd);
 
               if (true && sc >= 10 && (minors & bit::file(fl) & ~target) != 0) { // blocked by minor
 
                  sc = 5;
 
               }
 
 
 
               eval += sc - 10;
 
 
 
               if (sc >= 10 && std::abs(square::file(op_king) - fl) <= 1) { // open file on king
 
                  int weight = (square::file(op_king) == fl) ? 2 : 1;
 
                  mg += sc * weight / 2;
 
               }
 
            }
 
 
 
            if (pc == piece::ROOK && rk == square::RANK_7) { // 7th rank
 
 
 
               bit_t pawns = bd.piece(piece::PAWN, xd) & bit::rank(square::rank(sq));
 
 
 
               if (square::rank(op_king, sd) >= square::RANK_7 || pawns != 0) {
 
                  mg += 10;
 
                  eg += 20;
 
               }
 
            }
 
 
 
            if (pc == piece::KING) { // king out
 
 
 
               int dl = (pi.left_file - 1) - fl;
 
               if (dl > 0) eg -= dl * 20;
 
 
 
               int dr = fl - (pi.right_file + 1);
 
               if (dr > 0) eg -= dr * 20;
 
            }
 
         }
 
      }
 
 
 
      if (bd.count(piece::BISHOP, sd) >= 2) {
 
         mg += 30;
 
         eg += 50;
 
      }
 
 
 
      mg += shelter[sd];
 
      mg += mul_shift(king_score(king_power * 30, king_n), 32 - shelter[xd], 5);
 
 
 
      eval = -eval;
 
      mg = -mg;
 
      eg = -eg;
 
   }
 
 
 
   mg += pi.mg;
 
   eg += pi.eg;
 
 
 
   eval += eval_pattern(bd);
 
 
 
   eval += material::interpolation(mg, eg, bd);
 
 
 
   if (eval != 0) { // draw multiplier
 
      int winner = (eval > 0) ? side::WHITE : side::BLACK;
 
      eval = mul_shift(eval, draw_mul(winner, bd, pi), 4);
 
   }
 
 
 
   assert(eval >= score::EVAL_MIN && eval <= score::EVAL_MAX);
 
   return eval;
 
}
 
 
 
int eval(board::Board & bd, Table & table, pawn::Table & pawn_table) {
 
   return score::side_score(table.eval(bd, pawn_table), bd.turn());
 
}
 
 
 
void init() {
 
 
 
   small_centre  = 0;
 
   medium_centre = 0;
 
   large_centre  = 0;
 
 
 
   for (int sq = 0; sq < square::SIZE; sq++) {
 
 
 
      int fl = square::file(sq);
 
      int rk = square::rank(sq);
 
 
 
      if (fl >= square::FILE_D && fl <= square::FILE_E && rk >= square::RANK_4 && rk <= square::RANK_5) {
 
         bit::set(small_centre, sq);
 
      }
 
 
 
      if (fl >= square::FILE_C && fl <= square::FILE_F && rk >= square::RANK_3 && rk <= square::RANK_6) {
 
         bit::set(medium_centre, sq);
 
      }
 
 
 
      if (fl >= square::FILE_B && fl <= square::FILE_G && rk >= square::RANK_2 && rk <= square::RANK_7) {
 
         bit::set(large_centre, sq);
 
      }
 
   }
 
 
 
   large_centre  &= ~medium_centre;
 
   medium_centre &= ~small_centre;
 
 
 
   centre_0 = small_centre | large_centre;
 
   centre_1 = small_centre | medium_centre;
 
 
 
   side_area[side::WHITE] = 0;
 
   side_area[side::BLACK] = 0;
 
 
 
   for (int sq = 0; sq < square::SIZE; sq++) {
 
      if (square::rank(sq) <= square::RANK_4) {
 
         bit::set(side_area[side::WHITE], sq);
 
      } else {
 
         bit::set(side_area[side::BLACK], sq);
 
      }
 
   }
 
 
 
   for (int ks = 0; ks < square::SIZE; ks++) {
 
 
 
      king_area[side::WHITE][ks] = 0;
 
      king_area[side::BLACK][ks] = 0;
 
 
 
      for (int as = 0; as < square::SIZE; as++) {
 
 
 
         int df = square::file(as) - square::file(ks);
 
         int dr = square::rank(as) - square::rank(ks);
 
 
 
         if (std::abs(df) <= 1 && dr >= -1 && dr <= +2) {
 
            bit::set(king_area[side::WHITE][ks], as);
 
         }
 
 
 
         if (std::abs(df) <= 1 && dr >= -2 && dr <= +1) {
 
            bit::set(king_area[side::BLACK][ks], as);
 
         }
 
      }
 
   }
 
 
 
   for (int i = 0; i < 32; i++) {
 
      double x = double(i) * 0.5;
 
      double y = 1.0 - std::exp(-x);
 
      mob_weight[i] = util::round(y * 512.0) - 256;
 
   }
 
 
 
   for (int i = 0; i < 8; i++) {
 
      double x = double(i) - 3.0;
 
      double y = 1.0 / (1.0 + std::exp(-x));
 
      dist_weight[i] = util::round(y * 7.0 * 256.0);
 
   }
 
}
 
 
 
}
 
 
 
namespace uci {
 
 
 
bool infinite; // for UCI-design mistake :(
 
 
 
}
 
 
 
namespace search {
 
 
 
const int MAX_DEPTH = 100;
 
const int MAX_PLY = 100;
 
const int NODE_PERIOD = 1024;
 
 
 
const int MAX_THREADS = 16;
 
 
 
class Abort : public std::exception { // SP fail-high exception
 
 
 
};
 
 
 
void update_current ();
 
 
 
class PV {
 
 
 
private:
 
 
 
   static const int SIZE = MAX_PLY;
 
 
 
   int p_size;
 
   int p_move[SIZE];
 
 
 
public:
 
 
 
   PV() {
 
      clear();
 
   }
 
 
 
   void operator=(const PV & pv) {
 
      clear();
 
      add(pv);
 
   }
 
 
 
   void clear() {
 
      p_size = 0;
 
   }
 
 
 
   void add(int mv) {
 
      if (p_size < SIZE) {
 
         p_move[p_size++] = mv;
 
      }
 
   }
 
 
 
   void add(const PV & pv) {
 
      for (int pos = 0; pos < pv.size(); pos++) {
 
         int mv = pv.move(pos);
 
         add(mv);
 
      }
 
   }
 
 
 
   void cat(int mv, const PV & pv) {
 
      clear();
 
      add(mv);
 
      add(pv);
 
   }
 
 
 
   int size() const {
 
      return p_size;
 
   }
 
 
 
   int move(int pos) const {
 
      return p_move[pos];
 
   }
 
 
 
   std::string to_can() const {
 
 
 
      std::string s;
 
 
 
      for (int pos = 0; pos < size(); pos++) {
 
         int mv = move(pos);
 
         if (pos != 0) s += " ";
 
         s += move::to_can(mv);
 
      }
 
 
 
      return s;
 
   }
 
 
 
};
 
 
 
struct Time {
 
   bool depth_limited;
 
   bool node_limited;
 
   bool time_limited;
 
   int depth_limit;
 
   int64 node_limit;
 
   int64 time_limit;
 
   bool smart;
 
   bool ponder;
 
   bool flag;
 
   int64 limit_0;
 
   int64 limit_1;
 
   int64 limit_2;
 
   int last_score;
 
   bool drop;
 
   util::Timer timer;
 
};
 
 
 
struct Current {
 
 
 
   int depth;
 
   int max_ply;
 
   int64 node;
 
   int time;
 
   int speed;
 
 
 
   int move;
 
   int pos;
 
   int size;
 
   bool fail_high;
 
 
 
   int last_time;
 
};
 
 
 
struct Best {
 
   int depth;
 
   int move;
 
   int score;
 
   int flags;
 
   PV pv;
 
};
 
 
 
Time p_time;
 
Current current;
 
Best best;
 
 
 
class Search_Global : public util::Lockable {
 
 
 
public:
 
 
 
   trans::Table trans;
 
   sort::History history;
 
 
 
};
 
 
 
Search_Global sg;
 
 
 
class SMP : public util::Lockable {
 
 
 
};
 
 
 
SMP smp;
 
 
 
class Search_Local;
 
class Split_Point;
 
 
 
//void clear_iteration(Search_Local & sl); // Pierre-Marie Baty -- function not found
 
void search_root(Search_Local & sl, gen::List & ml, int depth, int alpha, int beta);
 
int search(Search_Local & sl, int depth, int alpha, int beta, PV & pv);
 
int split(Search_Local & sl, int depth, int old_alpha, int alpha, int beta, PV & pv, gen_sort::List & todo, const gen::List & done, int bs, int bm);
 
void master_split_point(Search_Local & sl, Split_Point & sp);
 
void search_split_point(Search_Local & sl, Split_Point & sp);
 
int qs_static(Search_Local & sl, int beta, int gain);
 
void inc_node(Search_Local & sl);
 
bool poll();
 
void move(Search_Local & sl, int mv);
 
void undo(Search_Local & sl);
 
int eval(Search_Local & sl);
 
int extension(Search_Local & sl, int mv, int depth, bool pv_node);
 
static int reduction(Search_Local & sl, int mv, int depth, bool pv_node, bool in_check, int searched_size, bool dangerous);
 
void gen_sort(Search_Local & sl, gen::List & ml);
 
 
 
void sg_abort ();
 
 
 
void          sl_init_early (Search_Local & sl, int id);
 
void          sl_init_late  (Search_Local & sl);
 
void          sl_set_root   (Search_Local & sl, const board::Board & bd);
 
void          sl_signal     (Search_Local & sl);
 
bool          sl_stop       (const Search_Local & sl);
 
bool          sl_idle       (const Search_Local & sl, Split_Point * sp);
 
void          sl_push       (Search_Local & sl, Split_Point & sp);
 
void          sl_pop        (Search_Local & sl);
 
Split_Point & sl_top        (const Search_Local & sl);
 
 
 
class Split_Point : public util::Lockable {
 
 
 
private:
 
 
 
   Search_Local * p_master;
 
   Split_Point * p_parent;
 
 
 
   board::Board p_board;
 
   int p_depth;
 
   int p_old_alpha;
 
   int volatile p_alpha;
 
   int p_beta;
 
 
 
   gen::List p_todo;
 
   gen::List p_done;
 
 
 
   int volatile p_workers;
 
   int volatile p_sent;
 
   int volatile p_received;
 
 
 
   int volatile p_bs;
 
   int volatile p_bm;
 
   PV p_pv;
 
 
 
public:
 
 
 
   void init_root(Search_Local & master) {
 
 
 
      p_master = &master;
 
      p_parent = NULL;
 
 
 
      p_bs = score::NONE;
 
      p_beta = score::MAX;
 
      p_todo.clear();
 
 
 
      p_workers = 1;
 
      p_received = -1; // HACK
 
   }
 
 
 
   void init(Search_Local & master, Split_Point & parent, const board::Board & bd, int depth, int old_alpha, int alpha, int beta, gen_sort::List & todo, const gen::List & done, int bs, int bm, const PV & pv) {
 
 
 
      assert(depth > 4);
 
      assert(old_alpha <= alpha);
 
      assert(alpha < beta);
 
      assert(done.size() != 0);
 
      assert(bs != score::NONE);
 
 
 
      p_master = &master;
 
      p_parent = &parent;
 
 
 
      p_board = bd;
 
      p_depth = depth;
 
      p_old_alpha = old_alpha;
 
      p_alpha = alpha;
 
      p_beta = beta;
 
 
 
      p_todo.clear();
 
 
 
      for (int mv = todo.next(); mv != move::NONE; mv = todo.next()) {
 
         p_todo.add(mv);
 
      }
 
 
 
      p_done = done;
 
 
 
      p_workers = 0;
 
      p_sent = 0;
 
      p_received = 0;
 
 
 
      p_bs = bs;
 
      p_bm = bm;
 
      p_pv = pv;
 
   }
 
 
 
   void enter() {
 
      lock();
 
      p_workers++;
 
      unlock();
 
   }
 
 
 
   void leave() {
 
 
 
      lock();
 
 
 
      assert(p_workers > 0);
 
      p_workers--;
 
 
 
      if (p_workers == 0) sl_signal(*p_master);
 
 
 
      unlock();
 
   }
 
 
 
   int next_move() {
 
 
 
      // lock();
 
 
 
      int mv = move::NONE;
 
 
 
      if (p_bs < p_beta && p_sent < p_todo.size()) {
 
         mv = p_todo.move(p_sent++);
 
      }
 
 
 
      // unlock();
 
 
 
      return mv;
 
   }
 
 
 
   void update_root() {
 
      lock();
 
      p_received = 0;
 
      p_workers = 0;
 
      unlock();
 
   }
 
 
 
   void update(int mv, int sc, const PV & pv) {
 
 
 
      lock();
 
 
 
      p_done.add(mv);
 
 
 
      assert(p_received < p_todo.size());
 
      p_received++;
 
 
 
      if (sc > p_bs) {
 
 
 
         p_bs = sc;
 
         p_pv.cat(mv, pv);
 
 
 
         if (sc > p_alpha) {
 
            p_bm = mv;
 
            p_alpha = sc;
 
         }
 
      }
 
 
 
      unlock();
 
   }
 
 
 
   const board::Board & board() const { return p_board; }
 
 
 
   Split_Point * parent() const { return p_parent; }
 
 
 
   int  depth()     const { return p_depth; }
 
   int  alpha()     const { return p_alpha; }
 
   int  beta()      const { return p_beta; }
 
   int  old_alpha() const { return p_old_alpha; }
 
   int  bs()        const { return p_bs; }
 
   int  bm()        const { return p_bm; }
 
   bool solved()    const { return p_bs >= p_beta || p_received == p_todo.size(); }
 
   bool free()      const { return p_workers == 0; }
 
 
 
   const gen::List & searched() const { return p_done; }
 
   int searched_size() const { return p_done.size(); }
 
 
 
   int result(PV & pv) const {
 
      pv = p_pv;
 
      return p_bs;
 
   }
 
 
 
};
 
 
 
Split_Point root_sp;
 
 
 
class Search_Local : public util::Waitable {
 
 
 
public:
 
 
 
   int id;
 
   std::thread thread;
 
 
 
   bool volatile todo;
 
   Split_Point * volatile todo_sp;
 
 
 
   board::Board board;
 
   sort::Killer killer;
 
   pawn::Table pawn_table;
 
   eval::Table eval_table;
 
 
 
   int64 volatile node;
 
   int volatile max_ply;
 
 
 
   Split_Point msp_stack[16];
 
   int msp_stack_size;
 
 
 
   Split_Point * ssp_stack[64];
 
   int ssp_stack_size;
 
};
 
 
 
Search_Local p_sl[MAX_THREADS];
 
 
 
void new_search() {
 
 
 
   p_time.depth_limited = true;
 
   p_time.node_limited = false;
 
   p_time.time_limited = false;
 
 
 
   p_time.depth_limit = MAX_DEPTH - 1;
 
 
 
   p_time.smart = false;
 
   p_time.ponder = false;
 
}
 
 
 
void set_depth_limit(int depth) {
 
   p_time.depth_limited = true;
 
   p_time.depth_limit = depth;
 
}
 
 
 
void set_node_limit(int64 node) {
 
   p_time.node_limited = true;
 
   p_time.node_limit = node;
 
}
 
 
 
void set_time_limit(int64 time) {
 
   p_time.time_limited = true;
 
   p_time.time_limit = time;
 
}
 
 
 
void set_ponder() {
 
   p_time.ponder = true;
 
}
 
 
 
void clear() {
 
 
 
   p_time.flag = false;
 
   p_time.timer.reset();
 
   p_time.timer.start();
 
 
 
   current.depth = 0;
 
   current.max_ply = 0;
 
   current.node = 0;
 
   current.time = 0;
 
   current.speed = 0;
 
 
 
   current.move = move::NONE;
 
   current.pos = 0;
 
   current.size = 0;
 
   current.fail_high = false;
 
 
 
   current.last_time = 0;
 
 
 
   best.depth = 0;
 
   best.move = move::NONE;
 
   best.score = score::NONE;
 
   best.flags = score::FLAGS_NONE;
 
   best.pv.clear();
 
}
 
 
 
void update_current() {
 
 
 
   int64 node = 0;
 
   int max_ply = 0;
 
 
 
   for (int id = 0; id < engine::engine.threads; id++) {
 
 
 
      Search_Local & sl = p_sl[id];
 
 
 
      node += sl.node;
 
      if (sl.max_ply > max_ply) max_ply = sl.max_ply;
 
   }
 
 
 
   current.node = node;
 
   current.max_ply = max_ply;
 
 
 
   current.time = p_time.timer.elapsed();
 
   current.speed = (current.time < 10) ? 0 : int(current.node * 1000 / current.time);
 
}
 
 
 
void write_pv(Best & best) {
 
 
 
   sg.lock();
 
 
 
   std::cout << "info";
 
   std::cout << " depth " << best.depth;
 
   std::cout << " seldepth " << current.max_ply;
 
   std::cout << " nodes " << current.node;
 
   std::cout << " time " << current.time;
 
 
 
   if (score::is_mate(best.score)) {
 
      std::cout << " score mate " << score::signed_mate(best.score);
 
   } else {
 
      std::cout << " score cp " << best.score;
 
   }
 
   if (best.flags == score::FLAGS_LOWER) std::cout << " lowerbound";
 
   if (best.flags == score::FLAGS_UPPER) std::cout << " upperbound";
 
 
 
   std::cout << " pv " << best.pv.to_can();
 
   std::cout << std::endl;
 
 
 
   sg.unlock();
 
}
 
 
 
void write_info() {
 
 
 
   sg.lock();
 
 
 
   std::cout << "info";
 
   std::cout << " depth " << current.depth;
 
   std::cout << " seldepth " << current.max_ply;
 
   std::cout << " currmove " << move::to_can(current.move);
 
   std::cout << " currmovenumber " << current.pos + 1;
 
   std::cout << " nodes " << current.node;
 
   std::cout << " time " << current.time;
 
   if (current.speed != 0) std::cout << " nps " << current.speed;
 
   std::cout << " hashfull " << sg.trans.used();
 
   std::cout << std::endl;
 
 
 
   sg.unlock();
 
}
 
 
 
void write_info_opt() {
 
 
 
   int time = current.time;
 
 
 
   if (time >= current.last_time + 1000) {
 
      write_info();
 
      current.last_time = time - time % 1000;
 
   }
 
}
 
 
 
void depth_start(int depth) {
 
   current.depth = depth;
 
}
 
 
 
void depth_end() {
 
}
 
 
 
void move_start(int mv, int pos, int size) {
 
 
 
   assert(size > 0);
 
   assert(pos < size);
 
 
 
   current.move = mv;
 
   current.pos = pos;
 
   current.size = size;
 
 
 
   current.fail_high = false;
 
}
 
 
 
void move_fail_high() {
 
   current.fail_high = true;
 
   p_time.flag = false;
 
}
 
 
 
void move_end() {
 
   current.fail_high = false;
 
}
 
 
 
void update_best(Best & best, int sc, int flags, const PV & pv) {
 
 
 
   assert(sc != score::NONE);
 
   assert(pv.size() != 0);
 
 
 
   p_time.drop = flags == score::FLAGS_UPPER || (sc <= p_time.last_score - 30 && current.size > 1);
 
 
 
   if (pv.move(0) != best.move || p_time.drop) {
 
      p_time.flag = false;
 
   }
 
 
 
   best.depth = current.depth;
 
   best.move = pv.move(0);
 
   best.score = sc;
 
   best.flags = flags;
 
   best.pv = pv;
 
}
 
 
 
void search_end() {
 
   p_time.timer.stop();
 
   update_current();
 
   write_info();
 
}
 
 
 
void idle_loop(Search_Local & sl, Split_Point & wait_sp) {
 
 
 
   sl_push(sl, wait_sp);
 
 
 
   while (true) {
 
 
 
      sl.lock();
 
 
 
      assert(sl.todo);
 
      assert(sl.todo_sp == NULL);
 
 
 
      sl.todo = false;
 
      sl.todo_sp = NULL;
 
 
 
      while (!wait_sp.free() && sl.todo_sp == NULL) {
 
         sl.wait();
 
      }
 
 
 
      Split_Point & sp = *sl.todo_sp;
 
      sl.todo = true;
 
      sl.todo_sp = NULL;
 
 
 
      sl.unlock();
 
 
 
      if (&sp == NULL) {
 
         break;
 
      }
 
 
 
      // sp.enter();
 
 
 
      sl_push(sl, sp);
 
 
 
      try {
 
         search_split_point(sl, sp);
 
      } catch (const Abort & /* abort */) {
 
         // no-op
 
      }
 
 
 
      sl_pop(sl);
 
 
 
      sp.leave();
 
   }
 
 
 
   sl_pop(sl);
 
}
 
 
 
void helper_program(Search_Local * sl) {
 
   sl_init_late(*sl);
 
   idle_loop(*sl, root_sp);
 
}
 
 
 
bool can_split(Search_Local & master, Split_Point & parent) {
 
 
 
   if (engine::engine.threads == 1) return false;
 
   if (master.msp_stack_size >= 16) return false;
 
   if (sl_stop(master)) return false;
 
 
 
   for (int id = 0; id < engine::engine.threads; id++) {
 
      Search_Local & worker = p_sl[id];
 
      if (&worker != &master && sl_idle(worker, &parent)) return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
void send_work(Search_Local & worker, Split_Point & sp) {
 
 
 
   worker.lock();
 
 
 
   if (sl_idle(worker, sp.parent())) {
 
 
 
      sp.enter();
 
 
 
      worker.todo = true;
 
      worker.todo_sp = &sp;
 
      worker.signal();
 
   }
 
 
 
   worker.unlock();
 
}
 
 
 
void init_sg() {
 
   sg.history.clear();
 
}
 
 
 
void search_root(Search_Local & sl, gen::List & ml, int depth, int alpha, int beta) {
 
 
 
   assert(depth > 0 && depth < MAX_DEPTH);
 
   assert(alpha < beta);
 
 
 
   board::Board & bd = sl.board;
 
   assert(attack::is_legal(bd));
 
 
 
   bool pv_node = true;
 
 
 
   int bs = score::NONE;
 
   int bm = move::NONE;
 
   int old_alpha = alpha;
 
 
 
   // transposition table
 
 
 
   hash_t key = 0;
 
 
 
   if (depth >= 0) {
 
      key = bd.key();
 
   }
 
 
 
   // move loop
 
 
 
   bool in_check = attack::is_in_check(bd);
 
 
 
   int searched_size = 0;
 
 
 
   for (int pos = 0; pos < ml.size(); pos++) {
 
 
 
      int mv = ml.move(pos);
 
 
 
      bool dangerous = in_check || move::is_tactical(mv) || move::is_check(mv, bd) || move::is_castling(mv) || move::is_pawn_push(mv, bd);
 
 
 
      int ext = extension(sl, mv, depth, pv_node);
 
      int red = reduction(sl, mv, depth, pv_node, in_check, searched_size, dangerous); // LMR
 
 
 
      if (ext != 0) red = 0;
 
      assert(ext == 0 || red == 0);
 
 
 
      int sc;
 
      PV npv;
 
 
 
      move_start(mv, pos, ml.size());
 
 
 
      move(sl, mv);
 
 
 
      if ((pv_node && searched_size != 0) || red != 0) {
 
 
 
         sc = -search(sl, depth + ext - red - 1, -alpha - 1, -alpha, npv);
 
 
 
         if (sc > alpha) { // PVS/LMR re-search
 
            move_fail_high();
 
            sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
         }
 
 
 
      } else {
 
 
 
         sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
      }
 
 
 
      undo(sl);
 
 
 
      move_end();
 
 
 
      searched_size++;
 
 
 
      if (sc > bs) {
 
 
 
         bs = sc;
 
 
 
         PV pv;
 
         pv.cat(mv, npv);
 
 
 
         update_best(best, sc, score::flags(sc, alpha, beta), pv);
 
 
 
         update_current();
 
         write_pv(best);
 
 
 
         if (sc > alpha) {
 
 
 
            bm = mv;
 
            alpha = sc;
 
 
 
            // ml.set_score(pos, sc); // not needed
 
            ml.move_to_front(pos);
 
 
 
            if (depth >= 0) {
 
               sg.trans.store(key, depth, bd.ply(), mv, sc, score::FLAGS_LOWER);
 
            }
 
 
 
            if (sc >= beta) return;
 
         }
 
      }
 
   }
 
 
 
   assert(bs != score::NONE);
 
   assert(bs < beta);
 
 
 
   if (depth >= 0) {
 
      int flags = score::flags(bs, old_alpha, beta);
 
      sg.trans.store(key, depth, bd.ply(), bm, bs, flags);
 
   }
 
}
 
 
 
int search(Search_Local & sl, int depth, int alpha, int beta, PV & pv) {
 
 
 
   assert(depth < MAX_DEPTH);
 
   assert(alpha < beta);
 
 
 
   board::Board & bd = sl.board;
 
   assert(attack::is_legal(bd));
 
 
 
   pv.clear();
 
 
 
   bool pv_node = depth > 0 && beta != alpha + 1;
 
 
 
   // mate-distance pruning
 
 
 
   {
 
      int sc = score::from_trans(score::MATE - 1, bd.ply());
 
 
 
      if (sc < beta) {
 
 
 
         beta = sc;
 
 
 
         if (sc <= alpha) {
 
            return sc;
 
         }
 
      }
 
   }
 
 
 
   // transposition table
 
 
 
   if (bd.is_draw()) return 0;
 
 
 
   attack::Attacks attacks;
 
   attack::init_attacks(attacks, bd);
 
   bool in_check = attacks.size != 0;
 
 
 
   bool use_trans = depth >= 0;
 
   int trans_depth = depth;
 
 
 
   if (depth < 0 && in_check) {
 
      use_trans = true;
 
      trans_depth = 0;
 
   }
 
 
 
   hash_t key = 0;
 
   int trans_move = move::NONE;
 
 
 
   if (use_trans) {
 
 
 
      key = bd.key();
 
 
 
      int score;
 
      int flags;
 
 
 
      if (sg.trans.retrieve(key, trans_depth, bd.ply(), trans_move, score, flags) && !pv_node) { // assigns trans_move #
 
         if (flags == score::FLAGS_LOWER && score >= beta)  return score;
 
         if (flags == score::FLAGS_UPPER && score <= alpha) return score;
 
         if (flags == score::FLAGS_EXACT) return score;
 
      }
 
   }
 
 
 
   // ply limit
 
 
 
   if (bd.ply() >= MAX_PLY) return eval(sl);
 
 
 
   // beta pruning
 
 
 
   if (!pv_node && depth > 0 && depth <= 3 && !score::is_mate(beta) && !in_check) {
 
 
 
      int sc = eval(sl) - depth * 50;
 
 
 
      if (sc >= beta) {
 
         return sc;
 
      }
 
   }
 
 
 
   // null-move pruning
 
 
 
   if (!pv_node && depth > 0 && !score::is_mate(beta) && !in_check && !material::lone_king(bd.turn(), bd) && eval(sl) >= beta) {
 
 
 
      bd.move_null(); // TODO: use sl?
 
 
 
       int sc = score::MIN;
 
 
 
      if (depth <= 3) { // static
 
         sc = -qs_static(sl, -beta + 1, 100);
 
      } else { // dynamic
 
         PV npv;
 
         sc = -search(sl, depth - 3 - 1, -beta, -beta + 1, npv);
 
      }
 
 
 
      bd.undo_null(); // TODO: use sl?
 
 
 
      if (sc >= beta) {
 
 
 
         if (use_trans) {
 
            sg.trans.store(key, trans_depth, bd.ply(), move::NONE, sc, score::FLAGS_LOWER);
 
         }
 
 
 
         return sc;
 
      }
 
   }
 
 
 
   // stand pat
 
 
 
   int bs = score::NONE;
 
   int bm = move::NONE;
 
   int old_alpha = alpha;
 
   int val = score::NONE; // for delta pruning
 
 
 
   if (depth <= 0 && !in_check) {
 
 
 
      bs = eval(sl);
 
      val = bs + 100; // QS-DP margin
 
 
 
      if (bs > alpha) {
 
 
 
         alpha = bs;
 
 
 
         if (bs >= beta) {
 
            return bs;
 
         }
 
      }
 
   }
 
 
 
   // futility-pruning condition
 
 
 
   bool use_fp = false;
 
 
 
   if (depth > 0 && depth <= 8 && !score::is_mate(alpha) && !in_check) {
 
 
 
      int sc = eval(sl) + depth * 40;
 
      val = sc + 50; // FP-DP margin, extra 50 for captures
 
 
 
      if (sc <= alpha) {
 
         bs = sc;
 
         use_fp = true;
 
      }
 
   }
 
 
 
   if (depth <= 0 && !in_check) { // unify FP and QS
 
      use_fp = true;
 
   }
 
 
 
   // IID
 
 
 
   if (pv_node && depth >= 3 && trans_move == move::NONE) {
 
 
 
      PV npv;
 
      int sc = search(sl, depth - 2, alpha, beta, npv); // to keep PV-node property
 
 
 
      if (sc > alpha && npv.size() != 0) {
 
         trans_move = npv.move(0);
 
      }
 
   }
 
 
 
   // move loop
 
 
 
   gen_sort::List ml;
 
   ml.init(depth, bd, attacks, trans_move, sl.killer, sg.history, use_fp);
 
 
 
   gen::List searched;
 
 
 
   for (int mv = ml.next(); mv != move::NONE; mv = ml.next()) {
 
 
 
      bool dangerous = in_check || move::is_tactical(mv) || move::is_check(mv, bd) || move::is_castling(mv) || move::is_pawn_push(mv, bd) || ml.is_candidate();
 
 
 
      if (use_fp && move::is_tactical(mv) && !move::is_check(mv, bd) && val + move::see_max(mv) <= alpha) { // delta pruning
 
         continue;
 
      }
 
 
 
      if (use_fp && !move::is_safe(mv, bd)) { // SEE pruning
 
         continue;
 
      }
 
 
 
      if (!pv_node && depth > 0 && depth <= 3 && !score::is_mate(bs) && searched.size() >= depth * 4 && !dangerous) { // late-move pruning
 
         continue;
 
      }
 
 
 
      int ext = extension(sl, mv, depth, pv_node);
 
      int red = reduction(sl, mv, depth, pv_node, in_check, searched.size(), dangerous); // LMR
 
 
 
      if (ext != 0) red = 0;
 
      assert(ext == 0 || red == 0);
 
 
 
      int sc;
 
      PV npv;
 
 
 
      move(sl, mv);
 
 
 
      if ((pv_node && searched.size() != 0) || red != 0) {
 
 
 
         sc = -search(sl, depth + ext - red - 1, -alpha - 1, -alpha, npv);
 
 
 
         if (sc > alpha) { // PVS/LMR re-search
 
            sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
         }
 
 
 
      } else {
 
 
 
         sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
      }
 
 
 
      undo(sl);
 
 
 
      searched.add(mv);
 
 
 
      if (sc > bs) {
 
 
 
         bs = sc;
 
         pv.cat(mv, npv);
 
 
 
         if (sc > alpha) {
 
 
 
            bm = mv;
 
            alpha = sc;
 
 
 
            if (use_trans) {
 
               sg.trans.store(key, trans_depth, bd.ply(), mv, sc, score::FLAGS_LOWER);
 
            }
 
 
 
            if (sc >= beta) {
 
 
 
               if (depth > 0 && !in_check && !move::is_tactical(mv)) {
 
                  sl.killer.add(mv, bd.ply());
 
                  sg.history.add(mv, searched, bd);
 
               }
 
 
 
               return sc;
 
            }
 
         }
 
      }
 
 
 
      if (depth >= 6 && !in_check && !use_fp && can_split(sl, sl_top(sl))) {
 
         return split(sl, depth, old_alpha, alpha, beta, pv, ml, searched, bs, bm);
 
      }
 
   }
 
 
 
   if (bs == score::NONE) {
 
      assert(depth > 0 || in_check);
 
      return in_check ? -score::MATE + bd.ply() : 0;
 
   }
 
 
 
   assert(bs < beta);
 
 
 
   if (use_trans) {
 
      int flags = score::flags(bs, old_alpha, beta);
 
      sg.trans.store(key, trans_depth, bd.ply(), bm, bs, flags);
 
   }
 
 
 
   return bs;
 
}
 
 
 
int split(Search_Local & master, int depth, int old_alpha, int alpha, int beta, PV & pv, gen_sort::List & todo, const gen::List & done, int bs, int bm) {
 
 
 
   smp.lock();
 
 
 
   assert(master.msp_stack_size < 16);
 
   Split_Point & sp = master.msp_stack[master.msp_stack_size++];
 
 
 
   Split_Point & parent = sl_top(master);
 
 
 
   sp.init(master, parent, master.board, depth, old_alpha, alpha, beta, todo, done, bs, bm, pv);
 
 
 
   for (int id = 0; id < engine::engine.threads; id++) {
 
 
 
      Search_Local & worker = p_sl[id];
 
 
 
      if (&worker != &master && sl_idle(worker, &parent)) {
 
         send_work(worker, sp);
 
      }
 
   }
 
 
 
   smp.unlock();
 
 
 
   try {
 
      master_split_point(master, sp);
 
   } catch (const Abort & /* abort */) {
 
      // no-op
 
   }
 
 
 
   assert(master.msp_stack_size > 0);
 
   assert(&master.msp_stack[master.msp_stack_size - 1] == &sp);
 
   master.msp_stack_size--;
 
 
 
   return sp.result(pv);
 
}
 
 
 
void master_split_point(Search_Local & sl, Split_Point & sp) {
 
 
 
   sp.enter();
 
 
 
   sl_push(sl, sp);
 
 
 
   try {
 
      search_split_point(sl, sp);
 
   } catch (const Abort & /* abort */) {
 
      // no-op
 
   }
 
 
 
   sl_pop(sl);
 
 
 
   sp.leave();
 
 
 
   idle_loop(sl, sp);
 
   sl.board = sp.board();
 
 
 
   assert(sp.free());
 
 
 
   // update move-ordering tables
 
 
 
   board::Board & bd = sl.board;
 
 
 
   int depth = sp.depth();
 
   int ply = bd.ply();
 
 
 
   int bs = sp.bs();
 
   int bm = sp.bm();
 
 
 
   assert(bs != score::NONE);
 
 
 
   if (bs >= sp.beta() && depth > 0 && !attack::is_in_check(bd) && !move::is_tactical(bm)) {
 
      sl.killer.add(bm, ply);
 
      sg.history.add(bm, sp.searched(), bd);
 
   }
 
 
 
   if (depth >= 0) {
 
      int flags = score::flags(bs, sp.old_alpha(), sp.beta());
 
      sg.trans.store(bd.key(), depth, ply, bm, bs, flags);
 
   }
 
}
 
 
 
void search_split_point(Search_Local & sl, Split_Point & sp) {
 
 
 
   board::Board & bd = sl.board;
 
   bd = sp.board();
 
 
 
   int depth = sp.depth();
 
   int old_alpha = sp.old_alpha();
 
   int beta = sp.beta();
 
 
 
   bool pv_node = depth > 0 && beta != old_alpha + 1;
 
 
 
   bool in_check = attack::is_in_check(bd);
 
 
 
   while (true) {
 
 
 
      sp.lock();
 
      int mv = sp.next_move();
 
      int alpha = sp.alpha();
 
      int searched_size = sp.searched_size();
 
      sp.unlock();
 
 
 
      if (mv == move::NONE) {
 
         break;
 
      }
 
 
 
      assert(alpha < beta);
 
 
 
      bool dangerous = in_check || move::is_tactical(mv) || move::is_check(mv, bd) || move::is_castling(mv) || move::is_pawn_push(mv, bd);
 
 
 
      int ext = extension(sl, mv, depth, pv_node);
 
      int red = reduction(sl, mv, depth, pv_node, in_check, searched_size, dangerous); // LMR
 
 
 
      if (ext != 0) red = 0;
 
      assert(ext == 0 || red == 0);
 
 
 
      int sc;
 
      PV npv;
 
 
 
      move(sl, mv);
 
 
 
      if ((pv_node && searched_size != 0) || red != 0) {
 
 
 
         sc = -search(sl, depth + ext - red - 1, -alpha - 1, -alpha, npv);
 
 
 
         if (sc > alpha) { // PVS/LMR re-search
 
            sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
         }
 
 
 
      } else {
 
 
 
         sc = -search(sl, depth + ext - 1, -beta, -alpha, npv);
 
      }
 
 
 
      undo(sl);
 
 
 
      sp.update(mv, sc, npv);
 
   }
 
}
 
 
 
int qs_static(Search_Local & sl, int beta, int gain) { // for static NMP
 
 
 
   board::Board & bd = sl.board;
 
 
 
   assert(attack::is_legal(bd));
 
   // assert(!attack::is_in_check()); // triggers for root move ordering
 
 
 
   // stand pat
 
 
 
   int bs = eval(sl);
 
   int val = bs + gain;
 
 
 
   if (bs >= beta) {
 
      return bs;
 
   }
 
 
 
   // move loop
 
 
 
   attack::Attacks attacks;
 
   attack::init_attacks(attacks, bd);
 
 
 
   gen_sort::List ml;
 
   ml.init(-1, bd, attacks, move::NONE, sl.killer, sg.history, false); // QS move generator
 
 
 
   bit_t done = 0;
 
 
 
   for (int mv = ml.next(); mv != move::NONE; mv = ml.next()) {
 
 
 
      if (bit::is_set(done, move::to(mv))) { // process only LVA capture for each opponent piece
 
         continue;
 
      }
 
 
 
      bit::set(done, move::to(mv));
 
 
 
      int see = move::see(mv, 0, score::EVAL_MAX, bd); // TODO: beta - val?
 
      if (see <= 0) continue; // don't consider equal captures as "threats"
 
 
 
      int sc = val + see;
 
 
 
      if (sc > bs) {
 
 
 
         bs = sc;
 
 
 
        if (sc >= beta) {
 
            return sc;
 
         }
 
      }
 
   }
 
 
 
   assert(bs < beta);
 
 
 
   return bs;
 
}
 
 
 
void inc_node(Search_Local & sl) {
 
 
 
   sl.node++;
 
 
 
   if (sl.node % NODE_PERIOD == 0) {
 
 
 
      bool abort = false;
 
 
 
      update_current();
 
 
 
      if (poll()) abort = true;
 
 
 
      if (p_time.node_limited && current.node >= p_time.node_limit) {
 
         abort = true;
 
      }
 
 
 
      if (p_time.time_limited && current.time >= p_time.time_limit) {
 
         abort = true;
 
      }
 
 
 
      if (p_time.smart && current.depth > 1 && current.time >= p_time.limit_0) {
 
         if (current.pos == 0 || current.time >= p_time.limit_1) {
 
            if (!(p_time.drop || current.fail_high) || current.time >= p_time.limit_2) {
 
               if (p_time.ponder) {
 
                  p_time.flag = true;
 
               } else {
 
                  abort = true;
 
               }
 
            }
 
         }
 
      }
 
 
 
      if (p_time.smart && current.depth > 1 && current.size == 1 && current.time >= p_time.limit_0 / 8) {
 
         if (p_time.ponder) {
 
            p_time.flag = true;
 
         } else {
 
            abort = true;
 
         }
 
      }
 
 
 
      if (abort) sg_abort();
 
   }
 
 
 
   if (sl_stop(sl)) throw Abort();
 
}
 
 
 
bool poll() {
 
 
 
   write_info_opt();
 
 
 
   sg.lock();
 
 
 
   if (!input::input.has_input()) {
 
      sg.unlock();
 
      return false;
 
   }
 
 
 
   std::string line;
 
   bool eof = !input::input.get_line(line);
 
   if (engine::engine.log) util::log(line);
 
 
 
   sg.unlock();
 
 
 
   if (false) {
 
   } else if (eof) {
 
      std::exit(EXIT_SUCCESS);
 
   } else if (line == "isready") {
 
      std::cout << "readyok" << std::endl;
 
      return false;
 
   } else if (line == "stop") {
 
      uci::infinite = false;
 
      return true;
 
   } else if (line == "ponderhit") {
 
      uci::infinite = false;
 
      p_time.ponder = false;
 
      return p_time.flag;
 
   } else if (line == "quit") {
 
#ifdef _MSC_VER
 
      ExitProcess (0); // Pierre-Marie Baty -- use ExitProcess() instead of std::exit() on Win32 apps
 
#else // !_MSC_VER
 
      std::exit (EXIT_SUCCESS);
 
#endif // _MSC_VER
 
   }
 
 
 
   return false;
 
}
 
 
 
void move(Search_Local & sl, int mv) {
 
 
 
   board::Board & bd = sl.board;
 
 
 
   inc_node(sl);
 
   bd.move(mv);
 
 
 
   int ply = bd.ply();
 
 
 
   if (ply > sl.max_ply) {
 
      assert(ply <= MAX_PLY);
 
      sl.max_ply = ply;
 
   }
 
}
 
 
 
void undo(Search_Local & sl) {
 
   board::Board & bd = sl.board;
 
   bd.undo();
 
}
 
 
 
int eval(Search_Local & sl) {
 
   board::Board & bd = sl.board;
 
   return eval::eval(bd, sl.eval_table, sl.pawn_table);
 
}
 
 
 
int extension(Search_Local & sl, int mv, int depth, bool pv_node) {
 
 
 
   board::Board & bd = sl.board;
 
 
 
   if ((depth <= 4 && move::is_check(mv, bd))
 
    || (depth <= 4 && move::is_recapture(mv, bd))
 
    || (pv_node && move::is_check(mv, bd))
 
    || (pv_node && move::is_tactical(mv) && move::is_win(mv, bd))
 
    || (pv_node && move::is_pawn_push(mv, bd))) {
 
      return 1;
 
   } else {
 
      return 0;
 
   }
 
}
 
 
 
int reduction(Search_Local & /* sl */, int /* mv */, int depth, bool /* pv_node */, bool /* in_check */, int searched_size, bool dangerous) {
 
 
 
   int red = 0;
 
 
 
   if (depth >= 3 && searched_size >= 3 && !dangerous) {
 
      red = (searched_size >= 6) ? depth / 3 : 1;
 
   }
 
 
 
   return red;
 
}
 
 
 
void gen_sort(Search_Local & sl, gen::List & ml) {
 
 
 
   board::Board & bd = sl.board;
 
 
 
   gen::gen_legals(ml, bd);
 
 
 
   int v = eval(sl);
 
 
 
   for (int pos = 0; pos < ml.size(); pos++) {
 
 
 
      int mv = ml.move(pos);
 
 
 
      move(sl, mv);
 
      int sc = -qs_static(sl, score::MAX, 0);
 
      undo(sl);
 
 
 
      sc = ((sc - v) / 4) + 1024; // HACK for unsigned 11-bit move-list scores
 
      assert(sc >= 0 && sc < move::SCORE_SIZE);
 
 
 
      ml.set_score(pos, sc);
 
   }
 
 
 
   ml.sort();
 
}
 
 
 
void sl_init_early(Search_Local & sl, int id) {
 
 
 
   sl.id = id;
 
 
 
   sl.todo = true;
 
   sl.todo_sp = NULL;
 
 
 
   sl.node = 0;
 
   sl.max_ply = 0;
 
 
 
   sl.msp_stack_size = 0;
 
   sl.ssp_stack_size = 0;
 
}
 
 
 
void sl_init_late(Search_Local & sl) {
 
   sl.killer.clear();
 
   sl.pawn_table.clear(); // pawn-eval cache
 
   sl.eval_table.clear(); // eval cache
 
}
 
 
 
void sl_set_root(Search_Local & sl, const board::Board & bd) {
 
   sl.board = bd;
 
   sl.board.set_root();
 
}
 
 
 
void sl_signal(Search_Local & sl) {
 
   sl.lock();
 
   sl.signal();
 
   sl.unlock();
 
}
 
 
 
bool sl_stop(const Search_Local & sl) {
 
 
 
   for (Split_Point * sp = &sl_top(sl); sp != NULL; sp = sp->parent()) {
 
      if (sp->solved()) return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
bool sl_idle(const Search_Local & worker, Split_Point * sp) {
 
 
 
   assert(sp != NULL);
 
 
 
   if (worker.todo) return false;
 
   if (worker.todo_sp != NULL) return false;
 
 
 
   Split_Point & wait_sp = sl_top(worker);
 
 
 
   for (; sp != NULL; sp = sp->parent()) {
 
      if (sp == &wait_sp) return true;
 
   }
 
 
 
   return false;
 
}
 
 
 
void sl_push(Search_Local & sl, Split_Point & sp) {
 
   assert(sl.ssp_stack_size < 16);
 
   sl.ssp_stack[sl.ssp_stack_size++] = &sp;
 
}
 
 
 
void sl_pop(Search_Local & sl) {
 
   assert(sl.ssp_stack_size > 0);
 
   sl.ssp_stack_size--;
 
}
 
 
 
Split_Point & sl_top(const Search_Local & sl) {
 
   assert(sl.ssp_stack_size > 0);
 
   return *sl.ssp_stack[sl.ssp_stack_size - 1];
 
}
 
 
 
void sg_abort() {
 
 
 
   root_sp.update_root();
 
 
 
   for (int id = 0; id < engine::engine.threads; id++) {
 
      sl_signal(p_sl[id]);
 
   }
 
}
 
 
 
void search_asp(gen::List & ml, int depth) {
 
 
 
   Search_Local & sl = p_sl[0];
 
 
 
   assert(depth <= 1 || p_time.last_score == best.score);
 
 
 
   if (depth >= 6 && !score::is_mate(p_time.last_score)) {
 
 
 
      for (int margin = 10; margin < 500; margin *= 2) {
 
 
 
         int a = p_time.last_score - margin;
 
         int b = p_time.last_score + margin;
 
         assert(score::EVAL_MIN <= a && a < b && b <= score::EVAL_MAX);
 
 
 
         search_root(sl, ml, depth, a, b);
 
 
 
         if (best.score > a && best.score < b) {
 
            return;
 
         } else if (score::is_mate(best.score)) {
 
            break;
 
         }
 
      }
 
   }
 
 
 
   search_root(sl, ml, depth, score::MIN, score::MAX);
 
}
 
 
 
void search_id(const board::Board & bd) {
 
 
 
   Search_Local & sl = p_sl[0];
 
 
 
   sl_set_root(sl, bd);
 
 
 
   sl_push(sl, root_sp);
 
 
 
   // move generation
 
 
 
   gen::List ml;
 
   gen_sort(sl, ml);
 
   assert(ml.size() != 0);
 
 
 
   best.move = ml.move(0);
 
   best.score = 0;
 
 
 
   bool easy = (ml.size() == 1 || (ml.size() > 1 && ml.score(0) - ml.score(1) >= 50 / 4)); // HACK: uses gen_sort() internals
 
   int easy_move = ml.move(0);
 
 
 
   p_time.last_score = score::NONE;
 
 
 
   // iterative deepening
 
 
 
   assert(p_time.depth_limited);
 
 
 
   for (int depth = 1; depth <= p_time.depth_limit; depth++) {
 
 
 
      depth_start(depth);
 
      search_asp(ml, depth);
 
      depth_end();
 
 
 
      // p_time.drop = (best.score <= p_time.last_score - 50); // moved to update_best()
 
      p_time.last_score = best.score;
 
 
 
      if (best.move != easy_move || p_time.drop) {
 
         easy = false;
 
      }
 
 
 
      if (p_time.smart && !p_time.drop) {
 
 
 
         bool abort = false;
 
 
 
         update_current();
 
 
 
         if (ml.size() == 1 && current.time >= p_time.limit_0 / 16) {
 
            abort = true;
 
         }
 
 
 
         if (easy && current.time >= p_time.limit_0 / 4) {
 
            abort = true;
 
         }
 
 
 
         if (current.time >= p_time.limit_0 / 2) {
 
            abort = true;
 
         }
 
 
 
         if (abort) {
 
            if (p_time.ponder) {
 
               p_time.flag = true;
 
            } else {
 
               break;
 
            }
 
         }
 
      }
 
   }
 
 
 
   sl_pop(sl);
 
}
 
 
 
void search_go(const board::Board & bd) {
 
 
 
   clear();
 
 
 
   init_sg();
 
   sg.trans.inc_date();
 
 
 
   for (int id = 0; id < engine::engine.threads; id++) {
 
      sl_init_early(p_sl[id], id);
 
   }
 
 
 
   root_sp.init_root(p_sl[0]);
 
 
 
   for (int id = 1; id < engine::engine.threads; id++) { // skip 0
 
      p_sl[id].thread = std::thread(helper_program, &p_sl[id]);
 
   }
 
 
 
   sl_init_late(p_sl[0]);
 
 
 
   try {
 
      search_id(bd);
 
   } catch (const Abort & /* abort */) {
 
      // no-op
 
   }
 
 
 
   sg_abort();
 
 
 
   for (int id = 1; id < engine::engine.threads; id++) { // skip 0
 
      p_sl[id].thread.join();
 
   }
 
 
 
   search_end();
 
}
 
 
 
void search_dumb(const board::Board & bd) {
 
 
 
   p_time.smart = false;
 
   p_time.last_score = score::NONE;
 
   p_time.drop = false;
 
 
 
   search_go(bd);
 
}
 
 
 
void search_smart(const board::Board & bd, int moves, int64 time, int64 inc) {
 
 
 
   if (moves == 0) moves = 40;
 
   moves = std::min(moves, material::interpolation(35, 15, bd));
 
   assert(moves > 0);
 
 
 
   int64 total = time + inc * (moves - 1);
 
   int factor = engine::engine.ponder ? 140 : 120;
 
   int64 alloc = total / moves * factor / 100;
 
   int64 reserve = total * (moves - 1) / 40;
 
   int64 max = std::min(time, total - reserve);
 
   max = std::min(max - 60, max * 95 / 100); // 60ms for lag
 
 
 
   alloc = std::max(alloc, I64(0));
 
   max = std::max(max, I64(0));
 
 
 
   p_time.smart = true;
 
   p_time.limit_0 = std::min(alloc, max);
 
   p_time.limit_1 = std::min(alloc * 4, max);
 
   p_time.limit_2 = max;
 
   p_time.last_score = score::NONE;
 
   p_time.drop = false;
 
 
 
   assert(0 <= p_time.limit_0 && p_time.limit_0 <= p_time.limit_1 && p_time.limit_1 <= p_time.limit_2);
 
 
 
   search_go(bd);
 
}
 
 
 
void init() {
 
   sg.trans.set_size(engine::engine.hash);
 
   sg.trans.alloc();
 
}
 
 
 
}
 
 
 
namespace uci {
 
 
 
board::Board bd;
 
bool delay;
 
 
 
class Scanner {
 
 
 
private:
 
 
 
  std::stringstream * p_ss;
 
  std::vector<std::string> p_keywords;
 
 
 
  bool p_undo;
 
  std::string p_word;
 
 
 
   bool is_keyword(const std::string & word) const {
 
 
 
      for (int i = 0; i < int(p_keywords.size()); i++) {
 
         if (p_keywords[i] == word) return true;
 
      }
 
 
 
      return false;
 
   }
 
 
 
public:
 
 
 
   Scanner(std::stringstream & ss) {
 
      p_ss = &ss;
 
      p_undo = false;
 
      add_keyword("");
 
   }
 
 
 
   void add_keyword(const std::string & keyword) {
 
      p_keywords.push_back(keyword);
 
   }
 
 
 
   std::string get_keyword() {
 
 
 
      std::string word = get_word();
 
      assert(is_keyword(word));
 
 
 
      return word;
 
   }
 
 
 
   std::string get_args() {
 
 
 
      std::string args;
 
      std::string word;
 
 
 
      while (true) {
 
 
 
         std::string word = get_word();
 
 
 
         if (is_keyword(word)) {
 
            unget_word();
 
            break;
 
         }
 
 
 
         if (args != "") args += " ";
 
         args += word;
 
      }
 
 
 
      return args;
 
   }
 
 
 
   std::string get_word() {
 
 
 
      if (p_undo) {
 
         p_undo = false;
 
      } else if (!(*p_ss >> p_word)) { // NOTE: reads as a side effect
 
         p_word = "";
 
      }
 
 
 
      return p_word;
 
   }
 
 
 
   void unget_word() {
 
      assert(!p_undo);
 
      p_undo = true;
 
   }
 
 
 
};
 
 
 
void fen(const std::string & fen) {
 
   bd.init_fen(fen);
 
}
 
 
 
void move(const std::string & move) {
 
   int mv = move::from_string(move, bd);
 
   bd.move(mv);
 
}
 
 
 
void send_bestmove() {
 
 
 
   std::cout << "bestmove " << move::to_can(search::best.move);
 
   if (search::best.pv.size() > 1) std::cout << " ponder " << move::to_can(search::best.pv.move(1));
 
   std::cout << std::endl;
 
 
 
   delay = false;
 
}
 
 
 
void command(Scanner & scan) {
 
 
 
   std::string command = scan.get_word();
 
 
 
   if (false) {
 
 
 
   } else if (command == "uci") {
 
 
 
      std::cout << "id name Senpai 1.0" << std::endl;
 
      std::cout << "id author Fabien Letouzey" << std::endl;
 
 
 
      std::cout << "option name Hash type spin default " << engine::engine.hash << " min 16 max 16384" << std::endl;
 
      std::cout << "option name Ponder type check default " << engine::engine.ponder << std::endl;
 
      std::cout << "option name Threads type spin default " << engine::engine.threads << " min 1 max 16" << std::endl;
 
      std::cout << "option name Log File type check default " << engine::engine.log << std::endl;
 
 
 
      std::cout << "uciok" << std::endl;
 
 
 
   } else if (command == "isready") {
 
 
 
      std::cout << "readyok" << std::endl;
 
 
 
   } else if (command == "setoption") {
 
 
 
      scan.add_keyword("name");
 
      scan.add_keyword("value");
 
 
 
      std::string name;
 
      std::string value;
 
 
 
      std::string part;
 
 
 
      while ((part = scan.get_keyword()) != "") {
 
 
 
         if (false) {
 
         } else if (part == "name") {
 
            name = scan.get_args();
 
         } else if (part == "value") {
 
            value = scan.get_args();
 
         }      
 
      }
 
 
 
      if (false) {
 
      } else if (util::string_case_equal(name, "Hash")) {
 
         engine::engine.hash = int(util::to_int(value));
 
         search::sg.trans.set_size(engine::engine.hash);
 
      } else if (util::string_case_equal(name, "Ponder")) {
 
         engine::engine.ponder = util::to_bool(value);
 
      } else if (util::string_case_equal(name, "Threads") || util::string_case_equal(name, "Cores")) {
 
         engine::engine.threads = int(util::to_int(value));
 
      } else if (util::string_case_equal(name, "Log File")) {
 
         engine::engine.log = util::to_bool(value);
 
      }
 
 
 
   } else if (command == "ucinewgame") {
 
 
 
      search::sg.trans.clear();
 
 
 
   } else if (command == "position") {
 
 
 
      scan.add_keyword("fen");
 
      scan.add_keyword("startpos");
 
      scan.add_keyword("moves");
 
 
 
      std::string part;
 
 
 
      while ((part = scan.get_keyword()) != "") {
 
 
 
         if (false) {
 
 
 
         } else if (part == "fen") {
 
 
 
            fen(scan.get_args());
 
 
 
         } else if (part == "startpos") {
 
 
 
            fen(board::start_fen);
 
 
 
         } else if (part == "moves") {
 
 
 
            std::string arg;
 
 
 
            while ((arg = scan.get_word()) != "") {
 
               uci::move(arg);
 
            }
 
         }
 
      }
 
 
 
   } else if (command == "go") {
 
 
 
      scan.add_keyword("searchmoves");
 
      scan.add_keyword("ponder");
 
      scan.add_keyword("wtime");
 
      scan.add_keyword("btime");
 
      scan.add_keyword("winc");
 
      scan.add_keyword("binc");
 
      scan.add_keyword("movestogo");
 
      scan.add_keyword("depth");
 
      scan.add_keyword("nodes");
 
      scan.add_keyword("mate");
 
      scan.add_keyword("movetime");
 
      scan.add_keyword("infinite");
 
 
 
      search::new_search();
 
 
 
      infinite = false;
 
      delay = false;
 
 
 
      bool smart = false;
 
      int time = 60000;
 
      int inc = 0;
 
      int movestogo = 0;
 
 
 
      std::string part;
 
 
 
      while ((part = scan.get_keyword()) != "") {
 
 
 
         std::string args = scan.get_args();
 
 
 
         if (false) {
 
 
 
         } else if (part == "ponder") {
 
 
 
            infinite = true;
 
            search::set_ponder();
 
 
 
         } else if (part == "wtime") {
 
 
 
            if (bd.turn() == side::WHITE) {
 
               smart = true;
 
               time = int(util::to_int(args));
 
            }
 
 
 
         } else if (part == "btime") {
 
 
 
            if (bd.turn() == side::BLACK) {
 
               smart = true;
 
               time = int(util::to_int(args));
 
            }
 
 
 
         } else if (part == "winc") {
 
 
 
            if (bd.turn() == side::WHITE) {
 
               smart = true;
 
               inc = int(util::to_int(args));
 
            }
 
 
 
         } else if (part == "binc") {
 
 
 
            if (bd.turn() == side::BLACK) {
 
               smart = true;
 
               inc = int(util::to_int(args));
 
            }
 
 
 
         } else if (part == "movestogo") {
 
 
 
            smart = true;
 
            movestogo = int(util::to_int(args));
 
 
 
         } else if (part == "depth") {
 
 
 
            search::set_depth_limit(int(util::to_int(args)));
 
 
 
         } else if (part == "nodes") {
 
 
 
            search::set_node_limit(util::to_int(args));
 
 
 
         } else if (part == "movetime") {
 
 
 
            search::set_time_limit(int(util::to_int(args)));
 
 
 
         } else if (part == "infinite") {
 
 
 
            infinite = true;
 
         }
 
      }
 
 
 
      if (smart) {
 
         search::search_smart(bd, movestogo, time, inc);
 
      } else {
 
         search::search_dumb(bd);
 
      }
 
 
 
      if (infinite) { // let's implement the UCI-design mistake :(
 
         delay = true;
 
      } else {
 
         send_bestmove();
 
      }
 
 
 
   } else if (command == "stop") {
 
 
 
      if (delay) send_bestmove();
 
 
 
   } else if (command == "ponderhit") {
 
 
 
      if (delay) send_bestmove();
 
 
 
   } else if (command == "quit") {
 
#ifdef _MSC_VER
 
      ExitProcess(0); // Pierre-Marie Baty -- use ExitProcess() instead of exit() on Win32 apps
 
#else // !_MSC_VER
 
      std::exit(EXIT_SUCCESS);
 
#endif // _MSC_VER
 
   }
 
}
 
 
 
void line(const std::string & line) {
 
 
 
   if (engine::engine.log) util::log(line);
 
 
 
   std::stringstream args(line);
 
   Scanner scan(args);
 
   command(scan);
 
}
 
 
 
void loop() {
 
 
 
   std::cout << std::boolalpha;
 
 
 
   infinite = false;
 
   delay = false;
 
 
 
   fen(board::start_fen);
 
 
 
   std::string line;
 
 
 
   while (input::input.get_line(line)) {
 
      uci::line(line);
 
   }
 
}
 
 
 
}
 
 
 
int main(int /* argc */, char * /* argv */ []) {
 
 
 
   assert(sizeof(uint8)  == 1);
 
   assert(sizeof(uint16) == 2);
 
   assert(sizeof(uint32) == 4);
 
   assert(sizeof(uint64) == 8);
 
 
 
   input::init();
 
   bit::init();
 
   hash::init();
 
   castling::init();
 
   attack::init();
 
   engine::init();
 
   material::init();
 
   pst::init();
 
   pawn::init();
 
   eval::init();
 
   search::init();
 
 
 
   uci::loop();
 
 
 
   return EXIT_SUCCESS;
 
}