Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Texel - A UCI chess engine.
  3.     Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
  4.  
  5.     This program is free software: you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation, either version 3 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. /*
  20.  * textio.cpp
  21.  *
  22.  *  Created on: Feb 25, 2012
  23.  *      Author: petero
  24.  */
  25.  
  26. #include "textio.hpp"
  27. #include "moveGen.hpp"
  28. #include <cassert>
  29.  
  30. const std::string TextIO::startPosFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
  31.  
  32.  
  33. Position
  34. TextIO::readFEN(const std::string& fen) {
  35.     Position pos;
  36.  
  37.     // Piece placement
  38.     int row = 7;
  39.     int col = 0;
  40.     size_t i;
  41.     for (i = 0; i < fen.length(); i++) {
  42.         char c = fen[i];
  43.         if (c == ' ')
  44.             break;
  45.         switch (c) {
  46.             case '1': col += 1; break;
  47.             case '2': col += 2; break;
  48.             case '3': col += 3; break;
  49.             case '4': col += 4; break;
  50.             case '5': col += 5; break;
  51.             case '6': col += 6; break;
  52.             case '7': col += 7; break;
  53.             case '8': col += 8; break;
  54.             case '/':
  55.                 row--; col = 0;
  56.                 if (row < 0) throw ChessParseError("Too many rows");
  57.                 break;
  58.             case 'P': safeSetPiece(pos, col, row, Piece::WPAWN);   col++; break;
  59.             case 'N': safeSetPiece(pos, col, row, Piece::WKNIGHT); col++; break;
  60.             case 'B': safeSetPiece(pos, col, row, Piece::WBISHOP); col++; break;
  61.             case 'R': safeSetPiece(pos, col, row, Piece::WROOK);   col++; break;
  62.             case 'Q': safeSetPiece(pos, col, row, Piece::WQUEEN);  col++; break;
  63.             case 'K': safeSetPiece(pos, col, row, Piece::WKING);   col++; break;
  64.             case 'p': safeSetPiece(pos, col, row, Piece::BPAWN);   col++; break;
  65.             case 'n': safeSetPiece(pos, col, row, Piece::BKNIGHT); col++; break;
  66.             case 'b': safeSetPiece(pos, col, row, Piece::BBISHOP); col++; break;
  67.             case 'r': safeSetPiece(pos, col, row, Piece::BROOK);   col++; break;
  68.             case 'q': safeSetPiece(pos, col, row, Piece::BQUEEN);  col++; break;
  69.             case 'k': safeSetPiece(pos, col, row, Piece::BKING);   col++; break;
  70.             default: throw ChessParseError("Invalid piece");
  71.         }
  72.     }
  73.     while (i < fen.length() && fen[i] == ' ')
  74.         i++;
  75.     if (i >= fen.length())
  76.         throw ChessParseError("Invalid side");
  77.     pos.setWhiteMove(fen[i++] == 'w');
  78.  
  79.     // Castling rights
  80.     int castleMask = 0;
  81.     while (i < fen.length() && fen[i] == ' ')
  82.         i++;
  83.     for ( ; i < fen.length(); i++) {
  84.         char c = fen[i];
  85.         if (c == ' ')
  86.             break;
  87.         switch (c) {
  88.         case 'K': castleMask |= (1 << Position::H1_CASTLE); break;
  89.         case 'Q': castleMask |= (1 << Position::A1_CASTLE); break;
  90.         case 'k': castleMask |= (1 << Position::H8_CASTLE); break;
  91.         case 'q': castleMask |= (1 << Position::A8_CASTLE); break;
  92.         case '-': break;
  93.         default: throw ChessParseError("Invalid castling flags");
  94.         }
  95.     }
  96.     pos.setCastleMask(castleMask);
  97.  
  98.     while (i < fen.length() && fen[i] == ' ')
  99.         i++;
  100.  
  101.     if (i < fen.length()) {
  102.         // En passant target square
  103.         if (fen[i] != '-') {
  104.             if (i >= fen.length() - 1)
  105.                 throw ChessParseError("Invalid en passant square");
  106.             pos.setEpSquare(getSquare(fen.substr(i, 2)));
  107.         }
  108.         while (i < fen.length() && fen[i] != ' ')
  109.             i++;
  110.     }
  111.  
  112.     while (i < fen.length() && fen[i] == ' ')
  113.         i++;
  114.     if (i < fen.length()) {
  115.         int i0 = i;
  116.         while (i < fen.length() && fen[i] != ' ')
  117.             i++;
  118.         int halfMoveClock;
  119.         if (str2Num(fen.substr(i0, i - i0), halfMoveClock))
  120.             pos.setHalfMoveClock(halfMoveClock);
  121.     }
  122.     while (i < fen.length() && fen[i] == ' ')
  123.         i++;
  124.     if (i < fen.length()) {
  125.         int i0 = i;
  126.         while (i < fen.length() && fen[i] != ' ')
  127.             i++;
  128.         int fullMoveCounter;
  129.         if (str2Num(fen.substr(i0, i - i0), fullMoveCounter))
  130.             pos.setFullMoveCounter(fullMoveCounter);
  131.     }
  132.  
  133.     // Each side must have exactly one king
  134.     int wKings = 0;
  135.     int bKings = 0;
  136.     for (int x = 0; x < 8; x++) {
  137.         for (int y = 0; y < 8; y++) {
  138.             int p = pos.getPiece(Position::getSquare(x, y));
  139.             if (p == Piece::WKING)
  140.                 wKings++;
  141.             else if (p == Piece::BKING)
  142.                 bKings++;
  143.         }
  144.     }
  145.     if (wKings != 1)
  146.         throw ChessParseError("White must have exactly one king");
  147.     if (bKings != 1)
  148.         throw ChessParseError("Black must have exactly one king");
  149.  
  150.     // Make sure king can not be captured
  151.     Position pos2(pos);
  152.     pos2.setWhiteMove(!pos.isWhiteMove());
  153.     if (MoveGen::inCheck(pos2))
  154.         throw ChessParseError("King capture possible");
  155.  
  156.     fixupEPSquare(pos);
  157.     return pos;
  158. }
  159.  
  160.  
  161. void
  162. TextIO::fixupEPSquare(Position& pos) {
  163.     int epSquare = pos.getEpSquare();
  164.     if (epSquare >= 0) {
  165.         MoveList moves;
  166.         MoveGen::pseudoLegalMoves(pos, moves);
  167.         MoveGen::removeIllegal(pos, moves);
  168.         bool epValid = false;
  169.         for (int mi = 0; mi < moves.size; mi++) {
  170.             const Move& m = moves[mi];
  171.             if (m.to() == epSquare) {
  172.                 if (pos.getPiece(m.from()) == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) {
  173.                     epValid = true;
  174.                     break;
  175.                 }
  176.             }
  177.         }
  178.         if (!epValid)
  179.             pos.setEpSquare(-1);
  180.     }
  181. }
  182.  
  183.  
  184. std::string
  185. TextIO::toFEN(const Position& pos) {
  186.     std::string ret;
  187.     // Piece placement
  188.     for (int r = 7; r >=0; r--) {
  189.         int numEmpty = 0;
  190.         for (int c = 0; c < 8; c++) {
  191.             int p = pos.getPiece(Position::getSquare(c, r));
  192.             if (p == Piece::EMPTY) {
  193.                 numEmpty++;
  194.             } else {
  195.                 if (numEmpty > 0) {
  196.                     ret += (char)('0' + numEmpty);
  197.                     numEmpty = 0;
  198.                 }
  199.                 switch (p) {
  200.                     case Piece::WKING:   ret += 'K'; break;
  201.                     case Piece::WQUEEN:  ret += 'Q'; break;
  202.                     case Piece::WROOK:   ret += 'R'; break;
  203.                     case Piece::WBISHOP: ret += 'B'; break;
  204.                     case Piece::WKNIGHT: ret += 'N'; break;
  205.                     case Piece::WPAWN:   ret += 'P'; break;
  206.                     case Piece::BKING:   ret += 'k'; break;
  207.                     case Piece::BQUEEN:  ret += 'q'; break;
  208.                     case Piece::BROOK:   ret += 'r'; break;
  209.                     case Piece::BBISHOP: ret += 'b'; break;
  210.                     case Piece::BKNIGHT: ret += 'n'; break;
  211.                     case Piece::BPAWN:   ret += 'p'; break;
  212.                     default: assert(false); break;
  213.                 }
  214.             }
  215.         }
  216.         if (numEmpty > 0)
  217.             ret += (char)('0' + numEmpty);
  218.         if (r > 0)
  219.             ret += '/';
  220.     }
  221.     ret += (pos.isWhiteMove() ? " w " : " b ");
  222.  
  223.     // Castling rights
  224.     bool anyCastle = false;
  225.     if (pos.h1Castle()) {
  226.         ret += 'K';
  227.         anyCastle = true;
  228.     }
  229.     if (pos.a1Castle()) {
  230.         ret += 'Q';
  231.         anyCastle = true;
  232.     }
  233.     if (pos.h8Castle()) {
  234.         ret += 'k';
  235.         anyCastle = true;
  236.     }
  237.     if (pos.a8Castle()) {
  238.         ret += 'q';
  239.         anyCastle = true;
  240.     }
  241.     if (!anyCastle) {
  242.         ret += '-';
  243.     }
  244.  
  245.     // En passant target square
  246.     {
  247.         ret += ' ';
  248.         if (pos.getEpSquare() >= 0) {
  249.             int x = Position::getX(pos.getEpSquare());
  250.             int y = Position::getY(pos.getEpSquare());
  251.             ret += ((char)(x + 'a'));
  252.             ret += ((char)(y + '1'));
  253.         } else {
  254.             ret += '-';
  255.         }
  256.     }
  257.  
  258.     // Move counters
  259.     ret += ' ';
  260.     ret += num2Str(pos.getHalfMoveClock());
  261.     ret += ' ';
  262.     ret += num2Str(pos.getFullMoveCounter());
  263.  
  264.     return ret;
  265. }
  266.  
  267. std::string
  268. TextIO::moveToUCIString(const Move& m) {
  269.     std::string ret = squareToString(m.from());
  270.     ret += squareToString(m.to());
  271.     switch (m.promoteTo()) {
  272.         case Piece::WQUEEN:
  273.         case Piece::BQUEEN:
  274.             ret += "q";
  275.             break;
  276.         case Piece::WROOK:
  277.         case Piece::BROOK:
  278.             ret += "r";
  279.             break;
  280.         case Piece::WBISHOP:
  281.         case Piece::BBISHOP:
  282.             ret += "b";
  283.             break;
  284.         case Piece::WKNIGHT:
  285.         case Piece::BKNIGHT:
  286.             ret += "n";
  287.             break;
  288.         default:
  289.             break;
  290.     }
  291.     return ret;
  292. }
  293.  
  294. Move
  295. TextIO::uciStringToMove(const std::string& move) {
  296.     Move m;
  297.     if ((move.length() < 4) || (move.length() > 5))
  298.         return m;
  299.     int fromSq = TextIO::getSquare(move.substr(0, 2));
  300.     int toSq   = TextIO::getSquare(move.substr(2, 2));
  301.     if ((fromSq < 0) || (toSq < 0)) {
  302.         return m;
  303.     }
  304.     char prom = ' ';
  305.     bool white = true;
  306.     if (move.length() == 5) {
  307.         prom = move[4];
  308.         if (Position::getY(toSq) == 7) {
  309.             white = true;
  310.         } else if (Position::getY(toSq) == 0) {
  311.             white = false;
  312.         } else {
  313.             return m;
  314.         }
  315.     }
  316.     int promoteTo;
  317.     switch (prom) {
  318.         case ' ':
  319.             promoteTo = Piece::EMPTY;
  320.             break;
  321.         case 'q':
  322.             promoteTo = white ? Piece::WQUEEN : Piece::BQUEEN;
  323.             break;
  324.         case 'r':
  325.             promoteTo = white ? Piece::WROOK : Piece::BROOK;
  326.             break;
  327.         case 'b':
  328.             promoteTo = white ? Piece::WBISHOP : Piece::BBISHOP;
  329.             break;
  330.         case 'n':
  331.             promoteTo = white ? Piece::WKNIGHT : Piece::BKNIGHT;
  332.             break;
  333.         default:
  334.             return m;
  335.     }
  336.     return Move(fromSq, toSq, promoteTo);
  337. }
  338.  
  339. static bool
  340. isCapture(const Position& pos, const Move& move) {
  341.     if (pos.getPiece(move.to()) != Piece::EMPTY)
  342.         return true;
  343.     int p = pos.getPiece(move.from());
  344.     return (p == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) &&
  345.            (move.to() == pos.getEpSquare());
  346. }
  347.  
  348. static std::string
  349. pieceToChar(int p) {
  350.     switch (p) {
  351.         case Piece::WQUEEN:  case Piece::BQUEEN:  return "Q";
  352.         case Piece::WROOK:   case Piece::BROOK:   return "R";
  353.         case Piece::WBISHOP: case Piece::BBISHOP: return "B";
  354.         case Piece::WKNIGHT: case Piece::BKNIGHT: return "N";
  355.         case Piece::WKING:   case Piece::BKING:   return "K";
  356.     }
  357.     return "";
  358. }
  359.  
  360. static std::string
  361. moveToString(Position& pos, const Move& move, bool longForm, const MoveList& moves) {
  362.     std::string ret;
  363.     int wKingOrigPos = Position::getSquare(4, 0);
  364.     int bKingOrigPos = Position::getSquare(4, 7);
  365.     if (move.from() == wKingOrigPos && pos.getPiece(wKingOrigPos) == Piece::WKING) {
  366.         // Check white castle
  367.         if (move.to() == Position::getSquare(6, 0))
  368.             ret += "O-O";
  369.         else if (move.to() == Position::getSquare(2, 0))
  370.             ret += "O-O-O";
  371.     } else if (move.from() == bKingOrigPos && pos.getPiece(bKingOrigPos) == Piece::BKING) {
  372.         // Check black castle
  373.         if (move.to() == Position::getSquare(6, 7))
  374.             ret += "O-O";
  375.         else if (move.to() == Position::getSquare(2, 7))
  376.             ret += "O-O-O";
  377.     }
  378.     if (ret.length() == 0) {
  379.         int p = pos.getPiece(move.from());
  380.         ret += pieceToChar(p);
  381.         int x1 = Position::getX(move.from());
  382.         int y1 = Position::getY(move.from());
  383.         int x2 = Position::getX(move.to());
  384.         int y2 = Position::getY(move.to());
  385.         if (longForm) {
  386.             ret += (char)(x1 + 'a');
  387.             ret += (char)(y1 + '1');
  388.             ret += isCapture(pos, move) ? 'x' : '-';
  389.         } else {
  390.             if (p == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) {
  391.                 if (isCapture(pos, move))
  392.                     ret += (char)(x1 + 'a');
  393.             } else {
  394.                 int numSameTarget = 0;
  395.                 int numSameFile = 0;
  396.                 int numSameRow = 0;
  397.                 for (int mi = 0; mi < moves.size; mi++) {
  398.                     const Move& m = moves[mi];
  399.                     if (m.isEmpty())
  400.                         break;
  401.                     if ((pos.getPiece(m.from()) == p) && (m.to() == move.to())) {
  402.                         numSameTarget++;
  403.                         if (Position::getX(m.from()) == x1)
  404.                             numSameFile++;
  405.                         if (Position::getY(m.from()) == y1)
  406.                             numSameRow++;
  407.                     }
  408.                 }
  409.                 if (numSameTarget < 2) {
  410.                     // No file/row info needed
  411.                 } else if (numSameFile < 2) {
  412.                     ret += (char)(x1 + 'a');   // Only file info needed
  413.                 } else if (numSameRow < 2) {
  414.                     ret += (char)(y1 + '1');   // Only row info needed
  415.                 } else {
  416.                     ret += (char) (x1 + 'a');   // File and row info needed
  417.                     ret += (char) (y1 + '1');
  418.                 }
  419.             }
  420.             if (isCapture(pos, move))
  421.                 ret += 'x';
  422.         }
  423.         ret += (char)(x2 + 'a');
  424.         ret += (char)(y2 + '1');
  425.         if (move.promoteTo() != Piece::EMPTY)
  426.             ret += pieceToChar(move.promoteTo());
  427.     }
  428.     UndoInfo ui;
  429.     if (MoveGen::givesCheck(pos, move)) {
  430.         pos.makeMove(move, ui);
  431.         MoveList nextMoves;
  432.         MoveGen::pseudoLegalMoves(pos, nextMoves);
  433.         MoveGen::removeIllegal(pos, nextMoves);
  434.         if (nextMoves.size == 0)
  435.             ret += '#';
  436.         else
  437.             ret += '+';
  438.         pos.unMakeMove(move, ui);
  439.     }
  440.  
  441.     return ret;
  442. }
  443.  
  444. std::string
  445. TextIO::moveToString(const Position& pos, const Move& move, bool longForm) {
  446.     MoveList moves;
  447.     MoveGen::pseudoLegalMoves(pos, moves);
  448.     Position tmpPos(pos);
  449.     MoveGen::removeIllegal(tmpPos, moves);
  450.     return ::moveToString(tmpPos, move, longForm, moves);
  451. }
  452.  
  453. namespace {
  454.     struct MoveInfo {
  455.         int piece = -1;             // -1 for unspecified
  456.         int fromX = -1, fromY = -1; // -1 for unspecified
  457.         int toX = -1, toY = -1;     // -1 for unspecified
  458.         int promPiece = -1;         // -1 for unspecified
  459.     };
  460. }
  461.  
  462. Move
  463. TextIO::stringToMove(Position& pos, const std::string& strMoveIn) {
  464.     std::string strMove;
  465.     for (size_t i = 0; i < strMoveIn.length(); i++) {
  466.         switch (strMoveIn[i]) {
  467.         case '=':
  468.         case '+':
  469.         case '#':
  470.             break;
  471.         default:
  472.             strMove += strMoveIn[i];
  473.             break;
  474.         }
  475.     }
  476.  
  477.     Move move;
  478.     if (strMove == "--")
  479.         return move;
  480.  
  481.     const bool wtm = pos.isWhiteMove();
  482.  
  483.     MoveInfo info;
  484.     bool capture = false;
  485.     if ((strMove == "O-O") || (strMove =="0-0") || (strMove == "o-o")) {
  486.         info.piece = wtm ? Piece::WKING : Piece::BKING;
  487.         info.fromX = 4;
  488.         info.toX = 6;
  489.         info.fromY = info.toY = wtm ? 0 : 7;
  490.         info.promPiece = Piece::EMPTY;
  491.     } else if ((strMove == "O-O-O") || (strMove == "0-0-0") || (strMove == "o-o-o")) {
  492.         info.piece = wtm ? Piece::WKING : Piece::BKING;
  493.         info.fromX = 4;
  494.         info.toX = 2;
  495.         info.fromY = info.toY = wtm ? 0 : 7;
  496.         info.promPiece = Piece::EMPTY;
  497.     } else {
  498.         bool atToSq = false;
  499.         for (size_t i = 0; i < strMove.length(); i++) {
  500.             char c = strMove[i];
  501.             if (i == 0) {
  502.                 int piece = charToPiece(wtm, c);
  503.                 if (piece >= 0) {
  504.                     info.piece = piece;
  505.                     continue;
  506.                 }
  507.             }
  508.             int tmpX = c - 'a';
  509.             if ((tmpX >= 0) && (tmpX < 8)) {
  510.                 if (atToSq || (info.fromX >= 0))
  511.                     info.toX = tmpX;
  512.                 else
  513.                     info.fromX = tmpX;
  514.             }
  515.             int tmpY = c - '1';
  516.             if ((tmpY >= 0) && (tmpY < 8)) {
  517.                 if (atToSq || (info.fromY >= 0))
  518.                     info.toY = tmpY;
  519.                 else
  520.                     info.fromY = tmpY;
  521.             }
  522.             if ((c == 'x') || (c == '-')) {
  523.                 atToSq = true;
  524.                 if (c == 'x')
  525.                     capture = true;
  526.             }
  527.             if (i == strMove.length() - 1) {
  528.                 int promPiece = charToPiece(wtm, c);
  529.                 if (promPiece >= 0) {
  530.                     info.promPiece = promPiece;
  531.                 }
  532.             }
  533.         }
  534.         if ((info.fromX >= 0) && (info.toX < 0)) {
  535.             info.toX = info.fromX;
  536.             info.fromX = -1;
  537.         }
  538.         if ((info.fromY >= 0) && (info.toY < 0)) {
  539.             info.toY = info.fromY;
  540.             info.fromY = -1;
  541.         }
  542.         if (info.piece < 0) {
  543.             bool haveAll = (info.fromX >= 0) && (info.fromY >= 0) &&
  544.                            (info.toX >= 0) && (info.toY >= 0);
  545.             if (!haveAll)
  546.                 info.piece = wtm ? Piece::WPAWN : Piece::BPAWN;
  547.         }
  548.         if (info.promPiece < 0)
  549.             info.promPiece = Piece::EMPTY;
  550.     }
  551.  
  552.     MoveList moves;
  553.     MoveGen::pseudoLegalMoves(pos, moves);
  554.     MoveGen::removeIllegal(pos, moves);
  555.  
  556.     std::vector<Move> matches;
  557.     for (int i = 0; i < moves.size; i++) {
  558.         const Move& m = moves[i];
  559.         int p = pos.getPiece(m.from());
  560.         bool match = true;
  561.         if ((info.piece >= 0) && (info.piece != p))
  562.             match = false;
  563.         if ((info.fromX >= 0) && (info.fromX != Position::getX(m.from())))
  564.             match = false;
  565.         if ((info.fromY >= 0) && (info.fromY != Position::getY(m.from())))
  566.             match = false;
  567.         if ((info.toX >= 0) && (info.toX != Position::getX(m.to())))
  568.             match = false;
  569.         if ((info.toY >= 0) && (info.toY != Position::getY(m.to())))
  570.             match = false;
  571.         if ((info.promPiece >= 0) && (info.promPiece != m.promoteTo()))
  572.             match = false;
  573.         if (match)
  574.             matches.push_back(m);
  575.     }
  576.     int nMatches = matches.size();
  577.     if (nMatches == 0)
  578.         return move;
  579.     else if (nMatches == 1)
  580.         return matches[0];
  581.     if (!capture)
  582.         return move;
  583.     for (size_t i = 0; i < matches.size(); i++) {
  584.         const Move& m = matches[i];
  585.         int capt = pos.getPiece(m.to());
  586.         if (capt != Piece::EMPTY) {
  587.             if (move.isEmpty()) {
  588.                 move = m;
  589.             } else {
  590.                 move = Move();
  591.                 return move;
  592.             }
  593.         }
  594.     }
  595.     return move;
  596. }
  597.  
  598. std::string
  599. TextIO::asciiBoard(const Position& pos) {
  600.     std::string ret;
  601.     ret += "    +----+----+----+----+----+----+----+----+\n";
  602.     for (int y = 7; y >= 0; y--) {
  603.         ret += "    |";
  604.         for (int x = 0; x < 8; x++) {
  605.             ret += ' ';
  606.             int p = pos.getPiece(Position::getSquare(x, y));
  607.             if (p == Piece::EMPTY) {
  608.                 bool dark = Position::darkSquare(x, y);
  609.                 ret.append(dark ? ".. |" : "   |");
  610.             } else {
  611.                 ret += Piece::isWhite(p) ? ' ' : '*';
  612.                 std::string pieceName = pieceToChar(p);
  613.                 if (pieceName.length() == 0)
  614.                     pieceName = "P";
  615.  
  616.                 ret += pieceName;
  617.                 ret += " |";
  618.             }
  619.         }
  620.  
  621.         ret += ("\n    +----+----+----+----+----+----+----+----+\n");
  622.     }
  623.  
  624.     return ret;
  625. }
  626.  
  627. std::string
  628. TextIO::asciiBoard(U64 mask) {
  629.     std::string ret;
  630.     for (int y = 7; y >= 0; y--) {
  631.         for (int x = 0; x < 8; x++) {
  632.             int sq = Position::getSquare(x, y);
  633.             ret += (mask & (1ULL << sq)) ? '1' : '0';
  634.         }
  635.         ret += '\n';
  636.     }
  637.     return ret;
  638. }
  639.