/*
Texel - A UCI chess engine.
Copyright (C) 2014 Peter Ă–sterlund, peterosterlund2@gmail.com
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/>.
*/
/*
* tbprobe.cpp
*
* Created on: Jun 2, 2014
* Author: petero
*/
#include "tbprobe.hpp"
#include "gtb/gtb-probe.h"
#include "syzygy/rtb-probe.hpp"
#include "bitBoard.hpp"
#include "position.hpp"
#include "moveGen.hpp"
#include "constants.hpp"
#include <unordered_map>
#include <cassert>
#include "util/timeUtil.hpp"
static std::string currentGtbPath;
static int currentGtbCacheMB;
static int currentGtbWdlFraction;
static std::string currentRtbPath;
static const char** gtbPaths = nullptr;
static int gtbMaxPieces = 0;
static std::unordered_map<int,int> maxDTM; // MatId -> Max DTM value in GTB TB
static std::unordered_map<int,int> maxDTZ; // MatId -> Max DTZ value in RTB TB
struct IIPairHash {
size_t operator()(const std::pair<int,int>& p) const {
return p.first * 0x714d3559 + p.second;
}
};
// (MatId,maxPawnMoves) -> Max DTM in sub TBs
static std::unordered_map<std::pair<int,int>,int,IIPairHash> maxSubDTM;
void
TBProbe::initialize(const std::string& gtbPath, int cacheMB,
const std::string& rtbPath) {
if (rtbPath != currentRtbPath) {
Syzygy::init(rtbPath);
currentRtbPath = rtbPath;
}
int wdlFraction = (Syzygy::TBLargest >= 5) ? 8 : 96;
if ((gtbPath != currentGtbPath) ||
(cacheMB != currentGtbCacheMB) ||
(wdlFraction != currentGtbWdlFraction)) {
gtbInitialize(gtbPath, cacheMB, wdlFraction);
currentGtbPath = gtbPath;
currentGtbCacheMB = cacheMB;
currentGtbWdlFraction = wdlFraction;
}
static bool initialized = false;
if (!initialized) {
initWDLBounds();
initialized = true;
}
}
bool
TBProbe::tbEnabled() {
return Syzygy::TBLargest > 0 || gtbMaxPieces > 0;
}
bool
TBProbe::tbProbe(Position& pos, int ply, int alpha, int beta,
TranspositionTable::TTEntry& ent) {
const int nPieces = BitBoard::bitCount(pos.occupiedBB());
bool mateSearch = SearchConst::isLoseScore(alpha) || SearchConst::isWinScore(beta);
if (mateSearch || pos.getHalfMoveClock() > 0) {
// Need DTM or DTZ probe
int dtmScore;
bool hasDtm = false;
if (nPieces <= gtbMaxPieces && gtbProbeDTM(pos, ply, dtmScore)) {
if (SearchConst::MATE0 - 1 - abs(dtmScore) - ply <= 100 - pos.getHalfMoveClock()) {
ent.setScore(dtmScore, ply);
ent.setType(TType::T_EXACT);
return true;
}
hasDtm = true;
}
int dtzScore;
if (nPieces <= Syzygy::TBLargest && rtbProbeDTZ(pos, ply, dtzScore)) {
ent.setScore(dtzScore, ply);
if (dtzScore > 0)
ent.setType(TType::T_GE);
else if (dtzScore < 0)
ent.setType(TType::T_LE);
else
ent.setType(TType::T_EXACT);
return true;
}
if (hasDtm) {
ent.setScore(dtmScore, ply);
ent.setType(TType::T_EXACT);
return true;
}
}
if (pos.getHalfMoveClock() == 0) {
// Try WDL probe if DTM/DTZ not needed or not available
int wdlScore;
if ((nPieces <= Syzygy::TBLargest && rtbProbeWDL(pos, ply, wdlScore)) ||
(nPieces <= gtbMaxPieces && gtbProbeWDL(pos, ply, wdlScore))) {
ent.setScore(wdlScore, ply);
if (wdlScore > 0)
ent.setType(TType::T_GE);
else if (wdlScore < 0)
ent.setType(TType::T_LE);
else
ent.setType(TType::T_EXACT);
return true;
}
}
return false;
}
bool
TBProbe::getSearchMoves(Position& pos, const MoveList& legalMoves,
std::vector<Move>& movesToSearch) {
const int mate0 = SearchConst::MATE0;
const int ply = 0;
TranspositionTable::TTEntry rootEnt;
if (!tbProbe(pos, ply, -mate0, mate0, rootEnt) || rootEnt.getType() == TType::T_LE)
return false;
const int rootScore = rootEnt.getScore(ply);
if (!SearchConst::isWinScore(rootScore))
return false;
// Root position is TB win
bool hasProgress = false;
UndoInfo ui;
for (int mi = 0; mi < legalMoves.size; mi++) {
const Move& m = legalMoves[mi];
pos.makeMove(m, ui);
TranspositionTable::TTEntry ent;
ent.clear();
bool progressMove = false;
bool badMove = false;
if (tbProbe(pos, ply+1, -mate0, mate0, ent)) {
const int type = ent.getType();
const int score = -ent.getScore(ply+1);
if (score >= rootScore && (type == TType::T_EXACT || type == TType::T_LE))
progressMove = true;
if ((score < rootScore)) // && (type == TType::T_EXACT || type == TType::T_GE))
badMove = true;
}
if (progressMove)
hasProgress = true;
if (!badMove)
movesToSearch.push_back(m);
pos.unMakeMove(m, ui);
}
return !hasProgress && !movesToSearch.empty();
}
void
TBProbe::extendPV(const Position& rootPos, std::vector<Move>& pv) {
Position pos(rootPos);
UndoInfo ui;
int ply = 0;
int score;
for (int i = 0; i < (int)pv.size(); i++) {
const Move& m = pv[i];
pos.makeMove(m, ui);
if (TBProbe::gtbProbeDTM(pos, ply, score) && SearchConst::isWinScore(std::abs(score)) &&
(SearchConst::MATE0 - 1 - abs(score) - ply <= 100 - pos.getHalfMoveClock())) {
// TB win, replace rest of PV since it may be inaccurate
pv.erase(pv.begin()+i+1, pv.end());
break;
}
}
if (!TBProbe::gtbProbeDTM(pos, ply, score) || !SearchConst::isWinScore(std::abs(score)))
return; // No TB win
if (SearchConst::MATE0 - 1 - abs(score) - ply > 100 - pos.getHalfMoveClock())
return; // Mate too far away, perhaps 50-move draw
if (!pos.isWhiteMove())
score = -score;
while (true) {
MoveList moveList;
MoveGen::pseudoLegalMoves(pos, moveList);
MoveGen::removeIllegal(pos, moveList);
bool extended = false;
for (int mi = 0; mi < moveList.size; mi++) {
const Move& m = moveList[mi];
pos.makeMove(m, ui);
int newScore;
if (TBProbe::gtbProbeDTM(pos, ply+1, newScore)) {
if (!pos.isWhiteMove())
newScore = -newScore;
if (newScore == score) {
pv.push_back(m);
ply++;
extended = true;
break;
}
}
pos.unMakeMove(m, ui);
}
if (!extended)
break;
}
}
template <typename ProbeFunc>
static void handleEP(Position& pos, int ply, int& score, bool& ret, ProbeFunc probeFunc) {
const bool inCheck = MoveGen::inCheck(pos);
MoveList moveList;
if (inCheck) MoveGen::checkEvasions(pos, moveList);
else MoveGen::pseudoLegalMoves(pos, moveList);
const int pawn = pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN;
int bestEP = std::numeric_limits<int>::min();
UndoInfo ui;
for (int m = 0; m < moveList.size; m++) {
const Move& move = moveList[m];
if ((move.to() == pos.getEpSquare()) && (pos.getPiece(move.from()) == pawn)) {
if (MoveGen::isLegal(pos, move, inCheck)) {
pos.makeMove(move, ui);
int score2;
bool ret2 = probeFunc(pos, ply+1, score2);
pos.unMakeMove(move, ui);
if (!ret2) {
ret = false;
return;
}
bestEP = std::max(bestEP, -score2);
}
} else if (MoveGen::isLegal(pos, move, inCheck))
return;
}
if (bestEP != std::numeric_limits<int>::min())
score = bestEP;
}
bool
TBProbe::gtbProbeDTM(Position& pos, int ply, int& score) {
if (BitBoard::bitCount(pos.occupiedBB()) > gtbMaxPieces)
return false;
GtbProbeData gtbData;
getGTBProbeData(pos, gtbData);
bool ret = gtbProbeDTM(gtbData, ply, score);
if (ret && score == 0 && pos.getEpSquare() != -1)
handleEP(pos, ply, score, ret, [](Position& pos, int ply, int& score) -> bool {
return TBProbe::gtbProbeDTM(pos, ply, score);
});
return ret;
}
bool
TBProbe::gtbProbeWDL(Position& pos, int ply, int& score) {
if (BitBoard::bitCount(pos.occupiedBB()) > gtbMaxPieces)
return false;
GtbProbeData gtbData;
getGTBProbeData(pos, gtbData);
bool ret = gtbProbeWDL(gtbData, ply, score);
if (ret && score == 0 && pos.getEpSquare() != -1)
handleEP(pos, ply, score, ret, [](Position& pos, int ply, int& score) -> bool {
return TBProbe::gtbProbeWDL(pos, ply, score);
});
return ret;
}
bool
TBProbe::rtbProbeDTZ(Position& pos, int ply, int& score) {
const int nPieces = BitBoard::bitCount(pos.occupiedBB());
if (nPieces > Syzygy::TBLargest)
return false;
if (pos.getCastleMask())
return false;
int success;
const int dtz = Syzygy::probe_dtz(pos, &success);
if (!success)
return false;
if (dtz == 0) {
score = 0;
return true;
}
const int maxHalfMoveClock = std::abs(dtz) + pos.getHalfMoveClock();
if (abs(dtz) <= 2) {
if (maxHalfMoveClock > 101) {
score = 0;
return true;
} else if (maxHalfMoveClock == 101)
return false; // DTZ can be wrong when mate-in-1
} else {
if (maxHalfMoveClock > 100) {
score = 0;
return true;
}
// FIXME!! Are there positions where maxHalfMoveclock==101 needs special handling?
}
int plyToMate = getMaxSubMate(pos) + std::abs(dtz);
if (dtz > 0) {
score = SearchConst::MATE0 - ply - plyToMate - 2;
} else {
score = -(SearchConst::MATE0 - ply - plyToMate - 2);
}
return true;
}
bool
TBProbe::rtbProbeWDL(Position& pos, int ply, int& score) {
if (BitBoard::bitCount(pos.occupiedBB()) > Syzygy::TBLargest)
return false;
if (pos.getCastleMask())
return false;
int success;
int wdl = Syzygy::probe_wdl(pos, &success);
if (!success)
return false;
int plyToMate;
switch (wdl) {
case 0: case 1: case -1:
score = 0;
break;
case 2:
plyToMate = getMaxSubMate(pos) + getMaxDTZ(pos.materialId());
score = SearchConst::MATE0 - ply - plyToMate - 2;
break;
case -2:
plyToMate = getMaxSubMate(pos) + getMaxDTZ(pos.materialId());
score = -(SearchConst::MATE0 - ply - plyToMate - 2);
break;
default:
return false;
}
return true;
}
void
TBProbe::gtbInitialize(const std::string& path, int cacheMB, int wdlFraction) {
static_assert((int)tb_A1 == (int)A1, "Incompatible square numbering");
static_assert((int)tb_A8 == (int)A8, "Incompatible square numbering");
static_assert((int)tb_H1 == (int)H1, "Incompatible square numbering");
static_assert((int)tb_H8 == (int)H8, "Incompatible square numbering");
tbpaths_done(gtbPaths);
gtbMaxPieces = 0;
gtbPaths = tbpaths_init();
gtbPaths = tbpaths_add(gtbPaths, path.c_str());
TB_compression_scheme scheme = tb_CP4;
int verbose = 0;
int cacheSize = 1024 * 1024 * cacheMB;
static bool isInitialized = false;
if (isInitialized) {
tb_restart(verbose, scheme, gtbPaths);
tbcache_restart(cacheSize, wdlFraction);
} else {
tb_init(verbose, scheme, gtbPaths);
tbcache_init(cacheSize, wdlFraction);
}
isInitialized = true;
unsigned int av = tb_availability();
if (av & 3)
gtbMaxPieces = 3;
if (av & 12)
gtbMaxPieces = 4;
if (av & 48)
gtbMaxPieces = 5;
}
void
TBProbe::getGTBProbeData(const Position& pos, GtbProbeData& gtbData) {
gtbData.stm = pos.isWhiteMove() ? tb_WHITE_TO_MOVE : tb_BLACK_TO_MOVE;
gtbData.epsq = pos.getEpSquare() >= 0 ? pos.getEpSquare() : tb_NOSQUARE;
gtbData.castles = 0;
if (pos.a1Castle()) gtbData.castles |= tb_WOOO;
if (pos.h1Castle()) gtbData.castles |= tb_WOO;
if (pos.a8Castle()) gtbData.castles |= tb_BOOO;
if (pos.h8Castle()) gtbData.castles |= tb_BOO;
int cnt = 0;
U64 m = pos.whiteBB();
while (m != 0) {
int sq = BitBoard::extractSquare(m);
gtbData.wSq[cnt] = sq;
switch (pos.getPiece(sq)) {
case Piece::WKING: gtbData.wP[cnt] = tb_KING; break;
case Piece::WQUEEN: gtbData.wP[cnt] = tb_QUEEN; break;
case Piece::WROOK: gtbData.wP[cnt] = tb_ROOK; break;
case Piece::WBISHOP: gtbData.wP[cnt] = tb_BISHOP; break;
case Piece::WKNIGHT: gtbData.wP[cnt] = tb_KNIGHT; break;
case Piece::WPAWN: gtbData.wP[cnt] = tb_PAWN; break;
default:
assert(false);
}
cnt++;
}
gtbData.wSq[cnt] = tb_NOSQUARE;
gtbData.wP[cnt] = tb_NOPIECE;
cnt = 0;
m = pos.blackBB();
while (m != 0) {
int sq = BitBoard::extractSquare(m);
gtbData.bSq[cnt] = sq;
switch (pos.getPiece(sq)) {
case Piece::BKING: gtbData.bP[cnt] = tb_KING; break;
case Piece::BQUEEN: gtbData.bP[cnt] = tb_QUEEN; break;
case Piece::BROOK: gtbData.bP[cnt] = tb_ROOK; break;
case Piece::BBISHOP: gtbData.bP[cnt] = tb_BISHOP; break;
case Piece::BKNIGHT: gtbData.bP[cnt] = tb_KNIGHT; break;
case Piece::BPAWN: gtbData.bP[cnt] = tb_PAWN; break;
default:
assert(false);
}
cnt++;
}
gtbData.bSq[cnt] = tb_NOSQUARE;
gtbData.bP[cnt] = tb_NOPIECE;
gtbData.materialId = pos.materialId();
}
bool
TBProbe::gtbProbeDTM(const GtbProbeData& gtbData, int ply, int& score) {
unsigned int tbInfo;
unsigned int plies;
if (!tb_probe_hard(gtbData.stm, gtbData.epsq, gtbData.castles,
gtbData.wSq, gtbData.bSq,
gtbData.wP, gtbData.bP,
&tbInfo, &plies))
return false;
switch (tbInfo) {
case tb_DRAW:
score = 0;
break;
case tb_WMATE:
score = SearchConst::MATE0 - ply - plies - 1;
break;
case tb_BMATE:
score = -(SearchConst::MATE0 - ply - (int)plies - 1); // Pierre-Marie Baty -- added type cast
break;
default:
return false;
};
if (gtbData.stm == tb_BLACK_TO_MOVE)
score = -score;
return true;
}
bool
TBProbe::gtbProbeWDL(const GtbProbeData& gtbData, int ply, int& score) {
unsigned int tbInfo;
if (!tb_probe_WDL_hard(gtbData.stm, gtbData.epsq, gtbData.castles,
gtbData.wSq, gtbData.bSq,
gtbData.wP, gtbData.bP,
&tbInfo))
return false;
switch (tbInfo) {
case tb_DRAW:
score = 0;
break;
case tb_WMATE:
score = maxDTM[gtbData.materialId] - ply;
break;
case tb_BMATE:
score = -(maxDTM[gtbData.materialId] - ply);
break;
default:
return false;
};
if (gtbData.stm == tb_BLACK_TO_MOVE)
score = -score;
return true;
}
void
TBProbe::initWDLBounds() {
initMaxDTM();
initMaxDTZ();
// Pre-calculate all interesting maxSubDTM values
for (int wp = 0; wp <= 4; wp++) {
std::vector<int> pieces(Piece::nPieceTypes);
pieces[Piece::WPAWN] = wp;
pieces[Piece::BPAWN] = 4 - wp;
getMaxSubMate(pieces, 4*5);
}
}
/** Maximum DTZ value for a given material configuration. */
int
TBProbe::getMaxDTZ(int matId) {
auto it = maxDTZ.find(matId);
if (it == maxDTZ.end())
return 100;
int val = it->second;
if (val < 0)
return 0;
else
return std::min(val+2, 100); // RTB DTZ values are not exact
}
static int
getMaxPawnMoves(const Position& pos) {
int maxPawnMoves = 0;
U64 m = pos.pieceTypeBB(Piece::WPAWN);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
maxPawnMoves += 6 - Position::getY(sq);
}
m = pos.pieceTypeBB(Piece::BPAWN);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
maxPawnMoves += Position::getY(sq) - 1;
}
return maxPawnMoves;
}
/** Get upper bound on longest mate in all possible material configurations
* after the next zeroing move. */
int
TBProbe::getMaxSubMate(const Position& pos) {
int maxPawnMoves = getMaxPawnMoves(pos);
int matId = pos.materialId();
matId = std::min(matId, MatId::mirror(matId));
auto it = maxSubDTM.find(std::make_pair(matId,maxPawnMoves));
if (it != maxSubDTM.end())
return it->second;
std::vector<int> pieces(Piece::nPieceTypes);
for (int p = 0; p < Piece::nPieceTypes; p++)
pieces[p] = BitBoard::bitCount(pos.pieceTypeBB((Piece::Type)p));
pieces[Piece::EMPTY] = pieces[Piece::WKING] = pieces[Piece::BKING] = 0;
return getMaxSubMate(pieces, maxPawnMoves);
}
int
TBProbe::getMaxSubMate(std::vector<int>& pieces, int pawnMoves) {
assert(pawnMoves >= 0);
if (pawnMoves > (pieces[Piece::WPAWN] + pieces[Piece::BPAWN]) * 5)
return 0;
MatId matId;
for (int p = 0; p < Piece::nPieceTypes; p++)
matId.addPieceCnt(p, pieces[p]);
const int matIdMin = std::min(matId(), MatId::mirror(matId()));
auto it = maxSubDTM.find(std::make_pair(matIdMin, pawnMoves));
if (it != maxSubDTM.end())
return it->second;
int maxSubMate = 0;
if (pawnMoves > 0) { // Pawn move
maxSubMate = getMaxSubMate(pieces, pawnMoves-1) + getMaxDTZ(matId());
}
for (int p = 0; p < Piece::nPieceTypes; p++) { // Capture
if (pieces[p] > 0) {
pieces[p]--;
matId.removePiece(p);
int maxRemovedPawnMoves = 0;
if (p == Piece::WPAWN || p == Piece::BPAWN)
maxRemovedPawnMoves = 5;
for (int i = 0; i <= maxRemovedPawnMoves; i++) {
int newPawnMoves = pawnMoves - i;
if (newPawnMoves >= 0) {
int tmp = getMaxSubMate(pieces, newPawnMoves) + getMaxDTZ(matId());
maxSubMate = std::max(maxSubMate, tmp);
}
}
pieces[p]++;
matId.addPiece(p);
}
}
for (int c = 0; c < 2; c++) { // Promotion
const int pawn = (c == 0) ? Piece::WPAWN : Piece::BPAWN;
if (pieces[pawn] > 0) {
const int p0 = (c == 0) ? Piece::WQUEEN : Piece::BQUEEN;
const int p1 = (c == 0) ? Piece::WKNIGHT : Piece::BKNIGHT;
for (int p = p0; p <= p1; p++) {
pieces[pawn]--;
pieces[p]++;
matId.removePiece(pawn);
matId.addPiece(p);
int tmp = getMaxSubMate(pieces, pawnMoves) + getMaxDTZ(matId());
maxSubMate = std::max(maxSubMate, tmp);
pieces[pawn]++;
pieces[p]--;
matId.addPiece(pawn);
matId.removePiece(p);
}
}
}
#if 0
std::cout << "wQ:" << pieces[Piece::WQUEEN]
<< " wR:" << pieces[Piece::WROOK]
<< " wB:" << pieces[Piece::WBISHOP]
<< " wN:" << pieces[Piece::WKNIGHT]
<< " wP:" << pieces[Piece::WPAWN]
<< " bQ:" << pieces[Piece::BQUEEN]
<< " bR:" << pieces[Piece::BROOK]
<< " bB:" << pieces[Piece::BBISHOP]
<< " bN:" << pieces[Piece::BKNIGHT]
<< " bP:" << pieces[Piece::BPAWN]
<< " pMoves:" << pawnMoves << " : " << maxSubMate << std::endl;
#endif
maxSubDTM[std::make_pair(matIdMin, pawnMoves)] = maxSubMate;
return maxSubMate;
}
void
TBProbe::initMaxDTM() {
using MI = MatId;
auto add = [](int id, int value) {
maxDTM[id] = value;
maxDTM[MatId::mirror(id)] = value;
};
add(MI::WQ, 31979);
add(MI::WR, 31967);
add(MI::WP, 31943);
add(MI::WQ*2, 31979);
add(MI::WQ+MI::WR, 31967);
add(MI::WQ+MI::WB, 31979);
add(MI::WQ+MI::WN, 31979);
add(MI::WQ+MI::WP, 31943);
add(MI::WR*2, 31967);
add(MI::WR+MI::WB, 31967);
add(MI::WR+MI::WN, 31967);
add(MI::WR+MI::WP, 31943);
add(MI::WB*2, 31961);
add(MI::WB+MI::WN, 31933);
add(MI::WB+MI::WP, 31937);
add(MI::WN*2, 31998);
add(MI::WN+MI::WP, 31943);
add(MI::WP*2, 31935);
add(MI::WQ+MI::BQ, 31974);
add(MI::WR+MI::BQ, 31929);
add(MI::WR+MI::BR, 31961);
add(MI::WB+MI::BQ, 31965);
add(MI::WB+MI::BR, 31941);
add(MI::WB+MI::BB, 31998);
add(MI::WN+MI::BQ, 31957);
add(MI::WN+MI::BR, 31919);
add(MI::WN+MI::BB, 31998);
add(MI::WN+MI::BN, 31998);
add(MI::WP+MI::BQ, 31942);
add(MI::WP+MI::BR, 31914);
add(MI::WP+MI::BB, 31942);
add(MI::WP+MI::BN, 31942);
add(MI::WP+MI::BP, 31933);
add(MI::WQ*3, 31991);
add(MI::WQ*2+MI::WR, 31987);
add(MI::WQ*2+MI::WB, 31983);
add(MI::WQ*2+MI::WN, 31981);
add(MI::WQ*2+MI::WP, 31979);
add(MI::WQ+MI::WR*2, 31985);
add(MI::WQ+MI::WR+MI::WB, 31967);
add(MI::WQ+MI::WR+MI::WN, 31967);
add(MI::WQ+MI::WR+MI::WP, 31967);
add(MI::WQ+MI::WB*2, 31961);
add(MI::WQ+MI::WB+MI::WN, 31933);
add(MI::WQ+MI::WB+MI::WP, 31937);
add(MI::WQ+MI::WN*2, 31981);
add(MI::WQ+MI::WN+MI::WP, 31945);
add(MI::WQ+MI::WP*2, 31935);
add(MI::WR*3, 31985);
add(MI::WR*2+MI::WB, 31967);
add(MI::WR*2+MI::WN, 31967);
add(MI::WR*2+MI::WP, 31967);
add(MI::WR+MI::WB*2, 31961);
add(MI::WR+MI::WB+MI::WN, 31933);
add(MI::WR+MI::WB+MI::WP, 31937);
add(MI::WR+MI::WN*2, 31967);
add(MI::WR+MI::WN+MI::WP, 31945);
add(MI::WR+MI::WP*2, 31935);
add(MI::WB*3, 31961);
add(MI::WB*2+MI::WN, 31933);
add(MI::WB*2+MI::WP, 31937);
add(MI::WB+MI::WN*2, 31931);
add(MI::WB+MI::WN+MI::WP, 31933);
add(MI::WB+MI::WP*2, 31935);
add(MI::WN*3, 31957);
add(MI::WN*2+MI::WP, 31943);
add(MI::WN+MI::WP*2, 31935);
add(MI::WP*3, 31933);
add(MI::WQ*2+MI::BQ, 31939);
add(MI::WQ*2+MI::BR, 31929);
add(MI::WQ*2+MI::BB, 31965);
add(MI::WQ*2+MI::BN, 31957);
add(MI::WQ*2+MI::BP, 31939);
add(MI::WQ+MI::WR+MI::BQ, 31865);
add(MI::WQ+MI::WR+MI::BR, 31929);
add(MI::WQ+MI::WR+MI::BB, 31941);
add(MI::WQ+MI::WR+MI::BN, 31919);
add(MI::WQ+MI::WR+MI::BP, 31865);
add(MI::WQ+MI::WB+MI::BQ, 31933);
add(MI::WQ+MI::WB+MI::BR, 31919);
add(MI::WQ+MI::WB+MI::BB, 31965);
add(MI::WQ+MI::WB+MI::BN, 31957);
add(MI::WQ+MI::WB+MI::BP, 31933);
add(MI::WQ+MI::WN+MI::BQ, 31917);
add(MI::WQ+MI::WN+MI::BR, 31918);
add(MI::WQ+MI::WN+MI::BB, 31965);
add(MI::WQ+MI::WN+MI::BN, 31957);
add(MI::WQ+MI::WN+MI::BP, 31917);
add(MI::WQ+MI::WP+MI::BQ, 31752);
add(MI::WQ+MI::WP+MI::BR, 31913);
add(MI::WQ+MI::WP+MI::BB, 31941);
add(MI::WQ+MI::WP+MI::BN, 31939);
add(MI::WQ+MI::WP+MI::BP, 31755);
add(MI::WR*2+MI::BQ, 31901);
add(MI::WR*2+MI::BR, 31937);
add(MI::WR*2+MI::BB, 31941);
add(MI::WR*2+MI::BN, 31919);
add(MI::WR*2+MI::BP, 31900);
add(MI::WR+MI::WB+MI::BQ, 31859);
add(MI::WR+MI::WB+MI::BR, 31870);
add(MI::WR+MI::WB+MI::BB, 31939);
add(MI::WR+MI::WB+MI::BN, 31919);
add(MI::WR+MI::WB+MI::BP, 31860);
add(MI::WR+MI::WN+MI::BQ, 31861);
add(MI::WR+MI::WN+MI::BR, 31918);
add(MI::WR+MI::WN+MI::BB, 31937);
add(MI::WR+MI::WN+MI::BN, 31919);
add(MI::WR+MI::WN+MI::BP, 31864);
add(MI::WR+MI::WP+MI::BQ, 31792);
add(MI::WR+MI::WP+MI::BR, 31851);
add(MI::WR+MI::WP+MI::BB, 31853);
add(MI::WR+MI::WP+MI::BN, 31891);
add(MI::WR+MI::WP+MI::BP, 31794);
add(MI::WB*2+MI::BQ, 31837);
add(MI::WB*2+MI::BR, 31938);
add(MI::WB*2+MI::BB, 31955);
add(MI::WB*2+MI::BN, 31843);
add(MI::WB*2+MI::BP, 31834);
add(MI::WB+MI::WN+MI::BQ, 31893);
add(MI::WB+MI::WN+MI::BR, 31918);
add(MI::WB+MI::WN+MI::BB, 31921);
add(MI::WB+MI::WN+MI::BN, 31786);
add(MI::WB+MI::WN+MI::BP, 31791);
add(MI::WB+MI::WP+MI::BQ, 31899);
add(MI::WB+MI::WP+MI::BR, 31910);
add(MI::WB+MI::WP+MI::BB, 31898);
add(MI::WB+MI::WP+MI::BN, 31800);
add(MI::WB+MI::WP+MI::BP, 31865);
add(MI::WN*2+MI::BQ, 31855);
add(MI::WN*2+MI::BR, 31918);
add(MI::WN*2+MI::BB, 31992);
add(MI::WN*2+MI::BN, 31986);
add(MI::WN*2+MI::BP, 31770);
add(MI::WN+MI::WP+MI::BQ, 31875);
add(MI::WN+MI::WP+MI::BR, 31866);
add(MI::WN+MI::WP+MI::BB, 31914);
add(MI::WN+MI::WP+MI::BN, 31805);
add(MI::WN+MI::WP+MI::BP, 31884);
add(MI::WP*2+MI::BQ, 31752);
add(MI::WP*2+MI::BR, 31892);
add(MI::WP*2+MI::BB, 31913);
add(MI::WP*2+MI::BN, 31899);
add(MI::WP*2+MI::BP, 31745);
}
void
TBProbe::initMaxDTZ() {
using MI = MatId;
auto add = [](int id, int value) {
maxDTZ[id] = value;
maxDTZ[MatId::mirror(id)] = value;
};
// 2-men
add(0, -1);
// 3-men
add(MI::WQ, 20);
add(MI::WR, 32);
add(MI::WB, -1);
add(MI::WN, -1);
add(MI::WP, 20);
// 4-men
add(MI::WQ+MI::BQ, 19);
add(MI::WN*2, 1);
add(MI::WQ*2, 6);
add(MI::WP*2, 14);
add(MI::WR*2, 10);
add(MI::WR+MI::BR, 7);
add(MI::WQ+MI::WB, 12);
add(MI::WQ+MI::WR, 8);
add(MI::WQ+MI::WN, 14);
add(MI::WR+MI::BB, 35);
add(MI::WB+MI::BB, 1);
add(MI::WQ+MI::WP, 6);
add(MI::WB*2, 37);
add(MI::WB+MI::BN, 2);
add(MI::WR+MI::WP, 6);
add(MI::WN+MI::BN, 1);
add(MI::WR+MI::BN, 53);
add(MI::WP+MI::BP, 21);
add(MI::WB+MI::BP, 7);
add(MI::WR+MI::WB, 24);
add(MI::WQ+MI::BN, 38);
add(MI::WR+MI::WN, 24);
add(MI::WB+MI::WP, 26);
add(MI::WN+MI::BP, 16);
add(MI::WN+MI::WP, 26);
add(MI::WQ+MI::BR, 62);
add(MI::WQ+MI::BB, 24);
add(MI::WR+MI::BP, 25);
add(MI::WQ+MI::BP, 52);
add(MI::WB+MI::WN, 65);
// 5-men
add(MI::WQ*3, 6);
add(MI::WQ*2+MI::WR, 6);
add(MI::WR*3, 8);
add(MI::WQ*2+MI::WB, 6);
add(MI::WQ*2+MI::WN, 8);
add(MI::WQ*2+MI::WP, 6);
add(MI::WQ+MI::WR+MI::WN, 8);
add(MI::WQ+MI::WR*2, 8);
add(MI::WQ+MI::WR+MI::WB, 8);
add(MI::WQ+MI::WP*2, 6);
add(MI::WQ+MI::WB+MI::WN, 8);
add(MI::WR*2+MI::WP, 6);
add(MI::WQ+MI::WB*2, 12);
add(MI::WB*3, 20);
add(MI::WR*2+MI::WN, 10);
add(MI::WR*2+MI::WB, 10);
add(MI::WQ+MI::WR+MI::WP, 6);
add(MI::WQ+MI::WN*2, 14);
add(MI::WQ+MI::WB+MI::WP, 6);
add(MI::WQ+MI::WN+MI::WP, 6);
add(MI::WR+MI::WP*2, 6);
add(MI::WR+MI::WB*2, 20);
add(MI::WP*3, 14);
add(MI::WR+MI::WN*2, 20);
add(MI::WQ*2+MI::BQ, 50);
add(MI::WQ*2+MI::BN, 8);
add(MI::WQ*2+MI::BB, 8);
add(MI::WR+MI::WB+MI::WN, 14);
add(MI::WB+MI::WP*2, 18);
add(MI::WB*2+MI::WP, 24);
add(MI::WQ*2+MI::BR, 28);
add(MI::WB*2+MI::WN, 26);
add(MI::WN+MI::WP*2, 12);
add(MI::WQ+MI::WB+MI::BQ, 59);
add(MI::WB+MI::WN*2, 26);
add(MI::WN*2+MI::WP, 16);
add(MI::WQ*2+MI::BP, 6);
add(MI::WN*3, 41);
add(MI::WQ+MI::WN+MI::BQ, 69);
add(MI::WQ+MI::WR+MI::BQ, 100);
add(MI::WQ+MI::WR+MI::BN, 10);
add(MI::WQ+MI::WR+MI::BB, 10);
add(MI::WQ+MI::WR+MI::BR, 30);
add(MI::WR+MI::WB+MI::WP, 8);
add(MI::WQ+MI::WB+MI::BN, 14);
add(MI::WQ+MI::WB+MI::BR, 38);
add(MI::WQ+MI::WB+MI::BB, 16);
add(MI::WB+MI::WN+MI::WP, 10);
add(MI::WR+MI::WN+MI::WP, 8);
add(MI::WR*2+MI::BQ, 40);
add(MI::WQ+MI::WN+MI::BN, 18);
add(MI::WR+MI::WB+MI::BR, 100);
add(MI::WQ+MI::WN+MI::BB, 18);
add(MI::WQ+MI::WR+MI::BP, 6);
add(MI::WR+MI::WB+MI::BQ, 82);
add(MI::WQ+MI::WP+MI::BQ, 100);
add(MI::WQ+MI::WP+MI::BP, 10);
add(MI::WQ+MI::WB+MI::BP, 22);
add(MI::WR+MI::WN+MI::BR, 64);
add(MI::WR*2+MI::BN, 14);
add(MI::WR*2+MI::BP, 18);
add(MI::WQ+MI::WN+MI::BR, 44);
add(MI::WR+MI::WN+MI::BQ, 92);
add(MI::WR*2+MI::BB, 20);
add(MI::WQ+MI::WN+MI::BP, 34);
add(MI::WR*2+MI::BR, 50);
add(MI::WB*2+MI::BR, 16);
add(MI::WB*2+MI::BB, 11);
add(MI::WQ+MI::WP+MI::BN, 12);
add(MI::WR+MI::WB+MI::BN, 42);
add(MI::WQ+MI::WP+MI::BB, 10);
add(MI::WB+MI::WN+MI::BR, 24);
add(MI::WB+MI::WN+MI::BB, 24);
add(MI::WB*2+MI::BN, 100);
add(MI::WB+MI::WN+MI::BN, 100);
add(MI::WQ+MI::WP+MI::BR, 34);
add(MI::WR+MI::WP+MI::BP, 19);
add(MI::WR+MI::WP+MI::BR, 70);
add(MI::WR+MI::WB+MI::BB, 50);
add(MI::WB*2+MI::BP, 42);
add(MI::WB*2+MI::BQ, 100);
add(MI::WR+MI::WB+MI::BP, 22);
add(MI::WN*2+MI::BR, 20);
add(MI::WN*2+MI::BB, 6);
add(MI::WB+MI::WP+MI::BR, 36);
add(MI::WN*2+MI::BN, 12);
add(MI::WB+MI::WP+MI::BB, 50);
add(MI::WR+MI::WN+MI::BN, 48);
add(MI::WN+MI::WP+MI::BR, 78);
add(MI::WN*2+MI::BQ, 100);
add(MI::WR+MI::WN+MI::BB, 50);
add(MI::WR+MI::WN+MI::BP, 29);
add(MI::WB+MI::WP+MI::BN, 60);
add(MI::WB+MI::WN+MI::BQ, 84);
add(MI::WB+MI::WP+MI::BP, 74);
add(MI::WN*2+MI::BP, 100);
add(MI::WN+MI::WP+MI::BB, 48);
add(MI::WP*2+MI::BB, 24);
add(MI::WP*2+MI::BQ, 58);
add(MI::WP*2+MI::BP, 42);
add(MI::WP*2+MI::BN, 27);
add(MI::WP*2+MI::BR, 30);
add(MI::WN+MI::WP+MI::BN, 59);
add(MI::WN+MI::WP+MI::BP, 46);
add(MI::WR+MI::WP+MI::BN, 62);
add(MI::WR+MI::WP+MI::BB, 100);
add(MI::WN+MI::WP+MI::BQ, 86);
add(MI::WB+MI::WN+MI::BP, 40);
add(MI::WR+MI::WP+MI::BQ, 100);
add(MI::WB+MI::WP+MI::BQ, 84);
// 6-men
add(MI::WB*4, 20);
add(MI::WB*3+MI::BB, 40);
add(MI::WB*3+MI::BN, 28);
add(MI::WB*3+MI::BP, 24);
add(MI::WB*3+MI::BQ, 100);
add(MI::WB*3+MI::BR, 100);
add(MI::WB*3+MI::WN, 26);
add(MI::WB*3+MI::WP, 24);
add(MI::WB*2+MI::BB*2, 11);
add(MI::WB*2+MI::BB+MI::BN, 40);
add(MI::WB*2+MI::BB+MI::BP, 69);
add(MI::WB*2+MI::BN*2, 56);
add(MI::WB*2+MI::BN+MI::BP, 100);
add(MI::WB*2+MI::BP*2, 39);
add(MI::WB*2+MI::WN+MI::BB, 72);
add(MI::WB*2+MI::WN+MI::BN, 62);
add(MI::WB*2+MI::WN+MI::BP, 32);
add(MI::WB*2+MI::WN+MI::BQ, 100);
add(MI::WB*2+MI::WN+MI::BR, 100);
add(MI::WB*2+MI::WN*2, 20);
add(MI::WB*2+MI::WN+MI::WP, 10);
add(MI::WB*2+MI::WP+MI::BB, 56);
add(MI::WB*2+MI::WP+MI::BN, 100);
add(MI::WB*2+MI::WP+MI::BP, 29);
add(MI::WB*2+MI::WP+MI::BQ, 100);
add(MI::WB*2+MI::WP+MI::BR, 100);
add(MI::WB*2+MI::WP*2, 12);
add(MI::WB+MI::WN+MI::BB+MI::BN, 17);
add(MI::WB+MI::WN+MI::BB+MI::BP, 56);
add(MI::WB+MI::WN+MI::BN*2, 24);
add(MI::WB+MI::WN+MI::BN+MI::BP, 98);
add(MI::WB+MI::WN+MI::BP*2, 48);
add(MI::WB+MI::WN*2+MI::BB, 76);
add(MI::WB+MI::WN*2+MI::BN, 58);
add(MI::WB+MI::WN*2+MI::BP, 33);
add(MI::WB+MI::WN*2+MI::BQ, 98);
add(MI::WB+MI::WN*2+MI::BR, 96);
add(MI::WB+MI::WN*3, 20);
add(MI::WB+MI::WN*2+MI::WP, 10);
add(MI::WB+MI::WN+MI::WP+MI::BB, 86);
add(MI::WB+MI::WN+MI::WP+MI::BN, 77);
add(MI::WB+MI::WN+MI::WP+MI::BP, 21);
add(MI::WB+MI::WN+MI::WP+MI::BQ, 100);
add(MI::WB+MI::WN+MI::WP+MI::BR, 100);
add(MI::WB+MI::WN+MI::WP*2, 10);
add(MI::WB+MI::WP+MI::BB+MI::BP, 65);
add(MI::WB+MI::WP+MI::BN*2, 48);
add(MI::WB+MI::WP+MI::BN+MI::BP, 62);
add(MI::WB+MI::WP+MI::BP*2, 75);
add(MI::WB+MI::WP*2+MI::BB, 86);
add(MI::WB+MI::WP*2+MI::BN, 100);
add(MI::WB+MI::WP*2+MI::BP, 61);
add(MI::WB+MI::WP*2+MI::BQ, 78);
add(MI::WB+MI::WP*2+MI::BR, 66);
add(MI::WB+MI::WP*3, 18);
add(MI::WN*2+MI::BN*2, 13);
add(MI::WN*2+MI::BN+MI::BP, 56);
add(MI::WN*2+MI::BP*2, 100);
add(MI::WN*3+MI::BB, 100);
add(MI::WN*3+MI::BN, 100);
add(MI::WN*3+MI::BP, 41);
add(MI::WN*3+MI::BQ, 70);
add(MI::WN*3+MI::BR, 22);
add(MI::WN*4, 22);
add(MI::WN*3+MI::WP, 12);
add(MI::WN*2+MI::WP+MI::BB, 100);
add(MI::WN*2+MI::WP+MI::BN, 100);
add(MI::WN*2+MI::WP+MI::BP, 33);
add(MI::WN*2+MI::WP+MI::BQ, 100);
add(MI::WN*2+MI::WP+MI::BR, 91);
add(MI::WN*2+MI::WP*2, 12);
add(MI::WN+MI::WP+MI::BN+MI::BP, 57);
add(MI::WN+MI::WP+MI::BP*2, 66);
add(MI::WN+MI::WP*2+MI::BB, 97);
add(MI::WN+MI::WP*2+MI::BN, 96);
add(MI::WN+MI::WP*2+MI::BP, 40);
add(MI::WN+MI::WP*2+MI::BQ, 78);
add(MI::WN+MI::WP*2+MI::BR, 81);
add(MI::WN+MI::WP*3, 10);
add(MI::WP*2+MI::BP*2, 31);
add(MI::WP*3+MI::BB, 36);
add(MI::WP*3+MI::BN, 42);
add(MI::WP*3+MI::BP, 40);
add(MI::WP*3+MI::BQ, 65);
add(MI::WP*3+MI::BR, 44);
add(MI::WP*4, 14);
add(MI::WQ+MI::WB*3, 12);
add(MI::WQ+MI::WB*2+MI::BB, 16);
add(MI::WQ+MI::WB*2+MI::BN, 14);
add(MI::WQ+MI::WB*2+MI::BP, 10);
add(MI::WQ+MI::WB*2+MI::BQ, 100);
add(MI::WQ+MI::WB*2+MI::BR, 40);
add(MI::WQ+MI::WB*2+MI::WN, 10);
add(MI::WQ+MI::WB*2+MI::WP, 6);
add(MI::WQ+MI::WB+MI::BB*2, 26);
add(MI::WQ+MI::WB+MI::BB+MI::BN, 32);
add(MI::WQ+MI::WB+MI::BB+MI::BP, 44);
add(MI::WQ+MI::WB+MI::BN*2, 26);
add(MI::WQ+MI::WB+MI::BN+MI::BP, 53);
add(MI::WQ+MI::WB+MI::BP*2, 34);
add(MI::WQ+MI::WB+MI::BQ+MI::BB, 91);
add(MI::WQ+MI::WB+MI::BQ+MI::BN, 72);
add(MI::WQ+MI::WB+MI::BQ+MI::BP, 100);
add(MI::WQ+MI::WB+MI::BR+MI::BB, 83);
add(MI::WQ+MI::WB+MI::BR+MI::BN, 54);
add(MI::WQ+MI::WB+MI::BR+MI::BP, 77);
add(MI::WQ+MI::WB+MI::BR*2, 100);
add(MI::WQ+MI::WB+MI::WN+MI::BB, 14);
add(MI::WQ+MI::WB+MI::WN+MI::BN, 12);
add(MI::WQ+MI::WB+MI::WN+MI::BP, 8);
add(MI::WQ+MI::WB+MI::WN+MI::BQ, 100);
add(MI::WQ+MI::WB+MI::WN+MI::BR, 44);
add(MI::WQ+MI::WB+MI::WN*2, 10);
add(MI::WQ+MI::WB+MI::WN+MI::WP, 6);
add(MI::WQ+MI::WB+MI::WP+MI::BB, 12);
add(MI::WQ+MI::WB+MI::WP+MI::BN, 12);
add(MI::WQ+MI::WB+MI::WP+MI::BP, 8);
add(MI::WQ+MI::WB+MI::WP+MI::BQ, 100);
add(MI::WQ+MI::WB+MI::WP+MI::BR, 62);
add(MI::WQ+MI::WB+MI::WP*2, 8);
add(MI::WQ+MI::WN+MI::BB*2, 30);
add(MI::WQ+MI::WN+MI::BB+MI::BN, 34);
add(MI::WQ+MI::WN+MI::BB+MI::BP, 67);
add(MI::WQ+MI::WN+MI::BN*2, 32);
add(MI::WQ+MI::WN+MI::BN+MI::BP, 62);
add(MI::WQ+MI::WN+MI::BP*2, 44);
add(MI::WQ+MI::WN+MI::BQ+MI::BN, 57);
add(MI::WQ+MI::WN+MI::BQ+MI::BP, 100);
add(MI::WQ+MI::WN+MI::BR+MI::BB, 52);
add(MI::WQ+MI::WN+MI::BR+MI::BN, 80);
add(MI::WQ+MI::WN+MI::BR+MI::BP, 83);
add(MI::WQ+MI::WN+MI::BR*2, 100);
add(MI::WQ+MI::WN*2+MI::BB, 22);
add(MI::WQ+MI::WN*2+MI::BN, 18);
add(MI::WQ+MI::WN*2+MI::BP, 20);
add(MI::WQ+MI::WN*2+MI::BQ, 100);
add(MI::WQ+MI::WN*2+MI::BR, 44);
add(MI::WQ+MI::WN*3, 10);
add(MI::WQ+MI::WN*2+MI::WP, 6);
add(MI::WQ+MI::WN+MI::WP+MI::BB, 12);
add(MI::WQ+MI::WN+MI::WP+MI::BN, 12);
add(MI::WQ+MI::WN+MI::WP+MI::BP, 12);
add(MI::WQ+MI::WN+MI::WP+MI::BQ, 100);
add(MI::WQ+MI::WN+MI::WP+MI::BR, 42);
add(MI::WQ+MI::WN+MI::WP*2, 10);
add(MI::WQ+MI::WP+MI::BB*2, 44);
add(MI::WQ+MI::WP+MI::BB+MI::BN, 36);
add(MI::WQ+MI::WP+MI::BB+MI::BP, 99);
add(MI::WQ+MI::WP+MI::BN*2, 92);
add(MI::WQ+MI::WP+MI::BN+MI::BP, 54);
add(MI::WQ+MI::WP+MI::BP*2, 35);
add(MI::WQ+MI::WP+MI::BQ+MI::BP, 100);
add(MI::WQ+MI::WP+MI::BR+MI::BB, 100);
add(MI::WQ+MI::WP+MI::BR+MI::BN, 100);
add(MI::WQ+MI::WP+MI::BR+MI::BP, 100);
add(MI::WQ+MI::WP+MI::BR*2, 100);
add(MI::WQ+MI::WP*2+MI::BB, 12);
add(MI::WQ+MI::WP*2+MI::BN, 12);
add(MI::WQ+MI::WP*2+MI::BP, 10);
add(MI::WQ+MI::WP*2+MI::BQ, 100);
add(MI::WQ+MI::WP*2+MI::BR, 42);
add(MI::WQ+MI::WP*3, 6);
add(MI::WQ*2+MI::WB*2, 6);
add(MI::WQ*2+MI::WB+MI::BB, 10);
add(MI::WQ*2+MI::WB+MI::BN, 10);
add(MI::WQ*2+MI::WB+MI::BP, 6);
add(MI::WQ*2+MI::WB+MI::BQ, 58);
add(MI::WQ*2+MI::WB+MI::BR, 52);
add(MI::WQ*2+MI::WB+MI::WN, 8);
add(MI::WQ*2+MI::WB+MI::WP, 6);
add(MI::WQ*2+MI::BB*2, 16);
add(MI::WQ*2+MI::BB+MI::BN, 16);
add(MI::WQ*2+MI::BB+MI::BP, 12);
add(MI::WQ*2+MI::BN*2, 14);
add(MI::WQ*2+MI::BN+MI::BP, 11);
add(MI::WQ*2+MI::BP*2, 6);
add(MI::WQ*2+MI::BQ+MI::BB, 100);
add(MI::WQ*2+MI::BQ+MI::BN, 100);
add(MI::WQ*2+MI::BQ+MI::BP, 79);
add(MI::WQ*2+MI::BQ*2, 87);
add(MI::WQ*2+MI::BQ+MI::BR, 100);
add(MI::WQ*2+MI::BR+MI::BB, 27);
add(MI::WQ*2+MI::BR+MI::BN, 28);
add(MI::WQ*2+MI::BR+MI::BP, 38);
add(MI::WQ*2+MI::BR*2, 36);
add(MI::WQ*2+MI::WN+MI::BB, 8);
add(MI::WQ*2+MI::WN+MI::BN, 10);
add(MI::WQ*2+MI::WN+MI::BP, 6);
add(MI::WQ*2+MI::WN+MI::BQ, 56);
add(MI::WQ*2+MI::WN+MI::BR, 48);
add(MI::WQ*2+MI::WN*2, 8);
add(MI::WQ*2+MI::WN+MI::WP, 6);
add(MI::WQ*2+MI::WP+MI::BB, 8);
add(MI::WQ*2+MI::WP+MI::BN, 10);
add(MI::WQ*2+MI::WP+MI::BP, 6);
add(MI::WQ*2+MI::WP+MI::BQ, 70);
add(MI::WQ*2+MI::WP+MI::BR, 48);
add(MI::WQ*2+MI::WP*2, 6);
add(MI::WQ*3+MI::WB, 6);
add(MI::WQ*3+MI::BB, 6);
add(MI::WQ*3+MI::BN, 8);
add(MI::WQ*3+MI::BP, 6);
add(MI::WQ*3+MI::BQ, 38);
add(MI::WQ*3+MI::BR, 40);
add(MI::WQ*3+MI::WN, 6);
add(MI::WQ*3+MI::WP, 6);
add(MI::WQ*4, 6);
add(MI::WQ*3+MI::WR, 6);
add(MI::WQ*2+MI::WR+MI::WB, 6);
add(MI::WQ*2+MI::WR+MI::BB, 8);
add(MI::WQ*2+MI::WR+MI::BN, 10);
add(MI::WQ*2+MI::WR+MI::BP, 6);
add(MI::WQ*2+MI::WR+MI::BQ, 56);
add(MI::WQ*2+MI::WR+MI::BR, 48);
add(MI::WQ*2+MI::WR+MI::WN, 8);
add(MI::WQ*2+MI::WR+MI::WP, 6);
add(MI::WQ*2+MI::WR*2, 6);
add(MI::WQ+MI::WR+MI::WB*2, 8);
add(MI::WQ+MI::WR+MI::WB+MI::BB, 10);
add(MI::WQ+MI::WR+MI::WB+MI::BN, 10);
add(MI::WQ+MI::WR+MI::WB+MI::BP, 6);
add(MI::WQ+MI::WR+MI::WB+MI::BQ, 98);
add(MI::WQ+MI::WR+MI::WB+MI::BR, 50);
add(MI::WQ+MI::WR+MI::WB+MI::WN, 8);
add(MI::WQ+MI::WR+MI::WB+MI::WP, 8);
add(MI::WQ+MI::WR+MI::BB*2, 24);
add(MI::WQ+MI::WR+MI::BB+MI::BN, 22);
add(MI::WQ+MI::WR+MI::BB+MI::BP, 28);
add(MI::WQ+MI::WR+MI::BN*2, 21);
add(MI::WQ+MI::WR+MI::BN+MI::BP, 26);
add(MI::WQ+MI::WR+MI::BP*2, 12);
add(MI::WQ+MI::WR+MI::BQ+MI::BB, 100);
add(MI::WQ+MI::WR+MI::BQ+MI::BN, 100);
add(MI::WQ+MI::WR+MI::BQ+MI::BP, 100);
add(MI::WQ+MI::WR+MI::BQ+MI::BR, 100);
add(MI::WQ+MI::WR+MI::BR+MI::BB, 42);
add(MI::WQ+MI::WR+MI::BR+MI::BN, 42);
add(MI::WQ+MI::WR+MI::BR+MI::BP, 44);
add(MI::WQ+MI::WR+MI::BR*2, 68);
add(MI::WQ+MI::WR+MI::WN+MI::BB, 8);
add(MI::WQ+MI::WR+MI::WN+MI::BN, 12);
add(MI::WQ+MI::WR+MI::WN+MI::BP, 7);
add(MI::WQ+MI::WR+MI::WN+MI::BQ, 100);
add(MI::WQ+MI::WR+MI::WN+MI::BR, 48);
add(MI::WQ+MI::WR+MI::WN*2, 8);
add(MI::WQ+MI::WR+MI::WN+MI::WP, 8);
add(MI::WQ+MI::WR+MI::WP+MI::BB, 8);
add(MI::WQ+MI::WR+MI::WP+MI::BN, 10);
add(MI::WQ+MI::WR+MI::WP+MI::BP, 7);
add(MI::WQ+MI::WR+MI::WP+MI::BQ, 100);
add(MI::WQ+MI::WR+MI::WP+MI::BR, 60);
add(MI::WQ+MI::WR+MI::WP*2, 6);
add(MI::WQ+MI::WR*2+MI::WB, 8);
add(MI::WQ+MI::WR*2+MI::BB, 8);
add(MI::WQ+MI::WR*2+MI::BN, 10);
add(MI::WQ+MI::WR*2+MI::BP, 6);
add(MI::WQ+MI::WR*2+MI::BQ, 82);
add(MI::WQ+MI::WR*2+MI::BR, 46);
add(MI::WQ+MI::WR*2+MI::WN, 8);
add(MI::WQ+MI::WR*2+MI::WP, 6);
add(MI::WQ+MI::WR*3, 8);
add(MI::WR+MI::WB*3, 20);
add(MI::WR+MI::WB*2+MI::BB, 36);
add(MI::WR+MI::WB*2+MI::BN, 23);
add(MI::WR+MI::WB*2+MI::BP, 24);
add(MI::WR+MI::WB*2+MI::BQ, 88);
add(MI::WR+MI::WB*2+MI::BR, 71);
add(MI::WR+MI::WB*2+MI::WN, 14);
add(MI::WR+MI::WB*2+MI::WP, 10);
add(MI::WR+MI::WB+MI::BB*2, 100);
add(MI::WR+MI::WB+MI::BB+MI::BN, 100);
add(MI::WR+MI::WB+MI::BB+MI::BP, 76);
add(MI::WR+MI::WB+MI::BN*2, 100);
add(MI::WR+MI::WB+MI::BN+MI::BP, 90);
add(MI::WR+MI::WB+MI::BP*2, 47);
add(MI::WR+MI::WB+MI::BR+MI::BB, 33);
add(MI::WR+MI::WB+MI::BR+MI::BN, 40);
add(MI::WR+MI::WB+MI::BR+MI::BP, 94);
add(MI::WR+MI::WB+MI::WN+MI::BB, 26);
add(MI::WR+MI::WB+MI::WN+MI::BN, 24);
add(MI::WR+MI::WB+MI::WN+MI::BP, 31);
add(MI::WR+MI::WB+MI::WN+MI::BQ, 100);
add(MI::WR+MI::WB+MI::WN+MI::BR, 72);
add(MI::WR+MI::WB+MI::WN*2, 14);
add(MI::WR+MI::WB+MI::WN+MI::WP, 10);
add(MI::WR+MI::WB+MI::WP+MI::BB, 20);
add(MI::WR+MI::WB+MI::WP+MI::BN, 20);
add(MI::WR+MI::WB+MI::WP+MI::BP, 21);
add(MI::WR+MI::WB+MI::WP+MI::BQ, 100);
add(MI::WR+MI::WB+MI::WP+MI::BR, 100);
add(MI::WR+MI::WB+MI::WP*2, 8);
add(MI::WR+MI::WN+MI::BB*2, 100);
add(MI::WR+MI::WN+MI::BB+MI::BN, 100);
add(MI::WR+MI::WN+MI::BB+MI::BP, 100);
add(MI::WR+MI::WN+MI::BN*2, 100);
add(MI::WR+MI::WN+MI::BN+MI::BP, 100);
add(MI::WR+MI::WN+MI::BP*2, 48);
add(MI::WR+MI::WN+MI::BR+MI::BN, 41);
add(MI::WR+MI::WN+MI::BR+MI::BP, 72);
add(MI::WR+MI::WN*2+MI::BB, 24);
add(MI::WR+MI::WN*2+MI::BN, 25);
add(MI::WR+MI::WN*2+MI::BP, 30);
add(MI::WR+MI::WN*2+MI::BQ, 81);
add(MI::WR+MI::WN*2+MI::BR, 78);
add(MI::WR+MI::WN*3, 14);
add(MI::WR+MI::WN*2+MI::WP, 8);
add(MI::WR+MI::WN+MI::WP+MI::BB, 26);
add(MI::WR+MI::WN+MI::WP+MI::BN, 20);
add(MI::WR+MI::WN+MI::WP+MI::BP, 27);
add(MI::WR+MI::WN+MI::WP+MI::BQ, 100);
add(MI::WR+MI::WN+MI::WP+MI::BR, 100);
add(MI::WR+MI::WN+MI::WP*2, 10);
add(MI::WR+MI::WP+MI::BB*2, 79);
add(MI::WR+MI::WP+MI::BB+MI::BN, 100);
add(MI::WR+MI::WP+MI::BB+MI::BP, 100);
add(MI::WR+MI::WP+MI::BN*2, 84);
add(MI::WR+MI::WP+MI::BN+MI::BP, 100);
add(MI::WR+MI::WP+MI::BP*2, 31);
add(MI::WR+MI::WP+MI::BR+MI::BP, 73);
add(MI::WR+MI::WP*2+MI::BB, 36);
add(MI::WR+MI::WP*2+MI::BN, 36);
add(MI::WR+MI::WP*2+MI::BP, 26);
add(MI::WR+MI::WP*2+MI::BQ, 100);
add(MI::WR+MI::WP*2+MI::BR, 90);
add(MI::WR+MI::WP*3, 6);
add(MI::WR*2+MI::WB*2, 12);
add(MI::WR*2+MI::WB+MI::BB, 14);
add(MI::WR*2+MI::WB+MI::BN, 12);
add(MI::WR*2+MI::WB+MI::BP, 8);
add(MI::WR*2+MI::WB+MI::BQ, 100);
add(MI::WR*2+MI::WB+MI::BR, 62);
add(MI::WR*2+MI::WB+MI::WN, 12);
add(MI::WR*2+MI::WB+MI::WP, 8);
add(MI::WR*2+MI::BB*2, 74);
add(MI::WR*2+MI::BB+MI::BN, 51);
add(MI::WR*2+MI::BB+MI::BP, 52);
add(MI::WR*2+MI::BN*2, 66);
add(MI::WR*2+MI::BN+MI::BP, 50);
add(MI::WR*2+MI::BP*2, 50);
add(MI::WR*2+MI::BR+MI::BB, 100);
add(MI::WR*2+MI::BR+MI::BN, 100);
add(MI::WR*2+MI::BR+MI::BP, 100);
add(MI::WR*2+MI::BR*2, 35);
add(MI::WR*2+MI::WN+MI::BB, 14);
add(MI::WR*2+MI::WN+MI::BN, 14);
add(MI::WR*2+MI::WN+MI::BP, 18);
add(MI::WR*2+MI::WN+MI::BQ, 100);
add(MI::WR*2+MI::WN+MI::BR, 66);
add(MI::WR*2+MI::WN*2, 12);
add(MI::WR*2+MI::WN+MI::WP, 8);
add(MI::WR*2+MI::WP+MI::BB, 14);
add(MI::WR*2+MI::WP+MI::BN, 12);
add(MI::WR*2+MI::WP+MI::BP, 22);
add(MI::WR*2+MI::WP+MI::BQ, 100);
add(MI::WR*2+MI::WP+MI::BR, 56);
add(MI::WR*2+MI::WP*2, 6);
add(MI::WR*3+MI::WB, 10);
add(MI::WR*3+MI::BB, 10);
add(MI::WR*3+MI::BN, 12);
add(MI::WR*3+MI::BP, 6);
add(MI::WR*3+MI::BQ, 100);
add(MI::WR*3+MI::BR, 42);
add(MI::WR*3+MI::WN, 10);
add(MI::WR*3+MI::WP, 8);
add(MI::WR*4, 8);
}