- /* 
-     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); 
- } 
-