- /* 
-     Texel - A UCI chess engine. 
-     Copyright (C) 2012-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/>. 
- */ 
-   
- /* 
-  * textio.cpp 
-  * 
-  *  Created on: Feb 25, 2012 
-  *      Author: petero 
-  */ 
-   
- #include "textio.hpp" 
- #include "moveGen.hpp" 
- #include <cassert> 
-   
- const std::string TextIO::startPosFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 
-   
-   
- Position 
- TextIO::readFEN(const std::string& fen) { 
-     Position pos; 
-   
-     // Piece placement 
-     int row = 7; 
-     int col = 0; 
-     size_t i; 
-     for (i = 0; i < fen.length(); i++) { 
-         char c = fen[i]; 
-         if (c == ' ') 
-             break; 
-         switch (c) { 
-             case '1': col += 1; break; 
-             case '2': col += 2; break; 
-             case '3': col += 3; break; 
-             case '4': col += 4; break; 
-             case '5': col += 5; break; 
-             case '6': col += 6; break; 
-             case '7': col += 7; break; 
-             case '8': col += 8; break; 
-             case '/': 
-                 row--; col = 0; 
-                 if (row < 0) throw ChessParseError("Too many rows"); 
-                 break; 
-             case 'P': safeSetPiece(pos, col, row, Piece::WPAWN);   col++; break; 
-             case 'N': safeSetPiece(pos, col, row, Piece::WKNIGHT); col++; break; 
-             case 'B': safeSetPiece(pos, col, row, Piece::WBISHOP); col++; break; 
-             case 'R': safeSetPiece(pos, col, row, Piece::WROOK);   col++; break; 
-             case 'Q': safeSetPiece(pos, col, row, Piece::WQUEEN);  col++; break; 
-             case 'K': safeSetPiece(pos, col, row, Piece::WKING);   col++; break; 
-             case 'p': safeSetPiece(pos, col, row, Piece::BPAWN);   col++; break; 
-             case 'n': safeSetPiece(pos, col, row, Piece::BKNIGHT); col++; break; 
-             case 'b': safeSetPiece(pos, col, row, Piece::BBISHOP); col++; break; 
-             case 'r': safeSetPiece(pos, col, row, Piece::BROOK);   col++; break; 
-             case 'q': safeSetPiece(pos, col, row, Piece::BQUEEN);  col++; break; 
-             case 'k': safeSetPiece(pos, col, row, Piece::BKING);   col++; break; 
-             default: throw ChessParseError("Invalid piece"); 
-         } 
-     } 
-     while (i < fen.length() && fen[i] == ' ') 
-         i++; 
-     if (i >= fen.length()) 
-         throw ChessParseError("Invalid side"); 
-     pos.setWhiteMove(fen[i++] == 'w'); 
-   
-     // Castling rights 
-     int castleMask = 0; 
-     while (i < fen.length() && fen[i] == ' ') 
-         i++; 
-     for ( ; i < fen.length(); i++) { 
-         char c = fen[i]; 
-         if (c == ' ') 
-             break; 
-         switch (c) { 
-         case 'K': castleMask |= (1 << Position::H1_CASTLE); break; 
-         case 'Q': castleMask |= (1 << Position::A1_CASTLE); break; 
-         case 'k': castleMask |= (1 << Position::H8_CASTLE); break; 
-         case 'q': castleMask |= (1 << Position::A8_CASTLE); break; 
-         case '-': break; 
-         default: throw ChessParseError("Invalid castling flags"); 
-         } 
-     } 
-     pos.setCastleMask(castleMask); 
-   
-     while (i < fen.length() && fen[i] == ' ') 
-         i++; 
-   
-     if (i < fen.length()) { 
-         // En passant target square 
-         if (fen[i] != '-') { 
-             if (i >= fen.length() - 1) 
-                 throw ChessParseError("Invalid en passant square"); 
-             pos.setEpSquare(getSquare(fen.substr(i, 2))); 
-         } 
-         while (i < fen.length() && fen[i] != ' ') 
-             i++; 
-     } 
-   
-     while (i < fen.length() && fen[i] == ' ') 
-         i++; 
-     if (i < fen.length()) { 
-         int i0 = i; 
-         while (i < fen.length() && fen[i] != ' ') 
-             i++; 
-         int halfMoveClock; 
-         if (str2Num(fen.substr(i0, i - i0), halfMoveClock)) 
-             pos.setHalfMoveClock(halfMoveClock); 
-     } 
-     while (i < fen.length() && fen[i] == ' ') 
-         i++; 
-     if (i < fen.length()) { 
-         int i0 = i; 
-         while (i < fen.length() && fen[i] != ' ') 
-             i++; 
-         int fullMoveCounter; 
-         if (str2Num(fen.substr(i0, i - i0), fullMoveCounter)) 
-             pos.setFullMoveCounter(fullMoveCounter); 
-     } 
-   
-     // Each side must have exactly one king 
-     int wKings = 0; 
-     int bKings = 0; 
-     for (int x = 0; x < 8; x++) { 
-         for (int y = 0; y < 8; y++) { 
-             int p = pos.getPiece(Position::getSquare(x, y)); 
-             if (p == Piece::WKING) 
-                 wKings++; 
-             else if (p == Piece::BKING) 
-                 bKings++; 
-         } 
-     } 
-     if (wKings != 1) 
-         throw ChessParseError("White must have exactly one king"); 
-     if (bKings != 1) 
-         throw ChessParseError("Black must have exactly one king"); 
-   
-     // Make sure king can not be captured 
-     Position pos2(pos); 
-     pos2.setWhiteMove(!pos.isWhiteMove()); 
-     if (MoveGen::inCheck(pos2)) 
-         throw ChessParseError("King capture possible"); 
-   
-     fixupEPSquare(pos); 
-     return pos; 
- } 
-   
-   
- void 
- TextIO::fixupEPSquare(Position& pos) { 
-     int epSquare = pos.getEpSquare(); 
-     if (epSquare >= 0) { 
-         MoveList moves; 
-         MoveGen::pseudoLegalMoves(pos, moves); 
-         MoveGen::removeIllegal(pos, moves); 
-         bool epValid = false; 
-         for (int mi = 0; mi < moves.size; mi++) { 
-             const Move& m = moves[mi]; 
-             if (m.to() == epSquare) { 
-                 if (pos.getPiece(m.from()) == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) { 
-                     epValid = true; 
-                     break; 
-                 } 
-             } 
-         } 
-         if (!epValid) 
-             pos.setEpSquare(-1); 
-     } 
- } 
-   
-   
- std::string 
- TextIO::toFEN(const Position& pos) { 
-     std::string ret; 
-     // Piece placement 
-     for (int r = 7; r >=0; r--) { 
-         int numEmpty = 0; 
-         for (int c = 0; c < 8; c++) { 
-             int p = pos.getPiece(Position::getSquare(c, r)); 
-             if (p == Piece::EMPTY) { 
-                 numEmpty++; 
-             } else { 
-                 if (numEmpty > 0) { 
-                     ret += (char)('0' + numEmpty); 
-                     numEmpty = 0; 
-                 } 
-                 switch (p) { 
-                     case Piece::WKING:   ret += 'K'; break; 
-                     case Piece::WQUEEN:  ret += 'Q'; break; 
-                     case Piece::WROOK:   ret += 'R'; break; 
-                     case Piece::WBISHOP: ret += 'B'; break; 
-                     case Piece::WKNIGHT: ret += 'N'; break; 
-                     case Piece::WPAWN:   ret += 'P'; break; 
-                     case Piece::BKING:   ret += 'k'; break; 
-                     case Piece::BQUEEN:  ret += 'q'; break; 
-                     case Piece::BROOK:   ret += 'r'; break; 
-                     case Piece::BBISHOP: ret += 'b'; break; 
-                     case Piece::BKNIGHT: ret += 'n'; break; 
-                     case Piece::BPAWN:   ret += 'p'; break; 
-                     default: assert(false); break; 
-                 } 
-             } 
-         } 
-         if (numEmpty > 0) 
-             ret += (char)('0' + numEmpty); 
-         if (r > 0) 
-             ret += '/'; 
-     } 
-     ret += (pos.isWhiteMove() ? " w " : " b "); 
-   
-     // Castling rights 
-     bool anyCastle = false; 
-     if (pos.h1Castle()) { 
-         ret += 'K'; 
-         anyCastle = true; 
-     } 
-     if (pos.a1Castle()) { 
-         ret += 'Q'; 
-         anyCastle = true; 
-     } 
-     if (pos.h8Castle()) { 
-         ret += 'k'; 
-         anyCastle = true; 
-     } 
-     if (pos.a8Castle()) { 
-         ret += 'q'; 
-         anyCastle = true; 
-     } 
-     if (!anyCastle) { 
-         ret += '-'; 
-     } 
-   
-     // En passant target square 
-     { 
-         ret += ' '; 
-         if (pos.getEpSquare() >= 0) { 
-             int x = Position::getX(pos.getEpSquare()); 
-             int y = Position::getY(pos.getEpSquare()); 
-             ret += ((char)(x + 'a')); 
-             ret += ((char)(y + '1')); 
-         } else { 
-             ret += '-'; 
-         } 
-     } 
-   
-     // Move counters 
-     ret += ' '; 
-     ret += num2Str(pos.getHalfMoveClock()); 
-     ret += ' '; 
-     ret += num2Str(pos.getFullMoveCounter()); 
-   
-     return ret; 
- } 
-   
- std::string 
- TextIO::moveToUCIString(const Move& m) { 
-     std::string ret = squareToString(m.from()); 
-     ret += squareToString(m.to()); 
-     switch (m.promoteTo()) { 
-         case Piece::WQUEEN: 
-         case Piece::BQUEEN: 
-             ret += "q"; 
-             break; 
-         case Piece::WROOK: 
-         case Piece::BROOK: 
-             ret += "r"; 
-             break; 
-         case Piece::WBISHOP: 
-         case Piece::BBISHOP: 
-             ret += "b"; 
-             break; 
-         case Piece::WKNIGHT: 
-         case Piece::BKNIGHT: 
-             ret += "n"; 
-             break; 
-         default: 
-             break; 
-     } 
-     return ret; 
- } 
-   
- Move 
- TextIO::uciStringToMove(const std::string& move) { 
-     Move m; 
-     if ((move.length() < 4) || (move.length() > 5)) 
-         return m; 
-     int fromSq = TextIO::getSquare(move.substr(0, 2)); 
-     int toSq   = TextIO::getSquare(move.substr(2, 2)); 
-     if ((fromSq < 0) || (toSq < 0)) { 
-         return m; 
-     } 
-     char prom = ' '; 
-     bool white = true; 
-     if (move.length() == 5) { 
-         prom = move[4]; 
-         if (Position::getY(toSq) == 7) { 
-             white = true; 
-         } else if (Position::getY(toSq) == 0) { 
-             white = false; 
-         } else { 
-             return m; 
-         } 
-     } 
-     int promoteTo; 
-     switch (prom) { 
-         case ' ': 
-             promoteTo = Piece::EMPTY; 
-             break; 
-         case 'q': 
-             promoteTo = white ? Piece::WQUEEN : Piece::BQUEEN; 
-             break; 
-         case 'r': 
-             promoteTo = white ? Piece::WROOK : Piece::BROOK; 
-             break; 
-         case 'b': 
-             promoteTo = white ? Piece::WBISHOP : Piece::BBISHOP; 
-             break; 
-         case 'n': 
-             promoteTo = white ? Piece::WKNIGHT : Piece::BKNIGHT; 
-             break; 
-         default: 
-             return m; 
-     } 
-     return Move(fromSq, toSq, promoteTo); 
- } 
-   
- static bool 
- isCapture(const Position& pos, const Move& move) { 
-     if (pos.getPiece(move.to()) != Piece::EMPTY) 
-         return true; 
-     int p = pos.getPiece(move.from()); 
-     return (p == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) && 
-            (move.to() == pos.getEpSquare()); 
- } 
-   
- static std::string 
- pieceToChar(int p) { 
-     switch (p) { 
-         case Piece::WQUEEN:  case Piece::BQUEEN:  return "Q"; 
-         case Piece::WROOK:   case Piece::BROOK:   return "R"; 
-         case Piece::WBISHOP: case Piece::BBISHOP: return "B"; 
-         case Piece::WKNIGHT: case Piece::BKNIGHT: return "N"; 
-         case Piece::WKING:   case Piece::BKING:   return "K"; 
-     } 
-     return ""; 
- } 
-   
- static std::string 
- moveToString(Position& pos, const Move& move, bool longForm, const MoveList& moves) { 
-     std::string ret; 
-     int wKingOrigPos = Position::getSquare(4, 0); 
-     int bKingOrigPos = Position::getSquare(4, 7); 
-     if (move.from() == wKingOrigPos && pos.getPiece(wKingOrigPos) == Piece::WKING) { 
-         // Check white castle 
-         if (move.to() == Position::getSquare(6, 0)) 
-             ret += "O-O"; 
-         else if (move.to() == Position::getSquare(2, 0)) 
-             ret += "O-O-O"; 
-     } else if (move.from() == bKingOrigPos && pos.getPiece(bKingOrigPos) == Piece::BKING) { 
-         // Check black castle 
-         if (move.to() == Position::getSquare(6, 7)) 
-             ret += "O-O"; 
-         else if (move.to() == Position::getSquare(2, 7)) 
-             ret += "O-O-O"; 
-     } 
-     if (ret.length() == 0) { 
-         int p = pos.getPiece(move.from()); 
-         ret += pieceToChar(p); 
-         int x1 = Position::getX(move.from()); 
-         int y1 = Position::getY(move.from()); 
-         int x2 = Position::getX(move.to()); 
-         int y2 = Position::getY(move.to()); 
-         if (longForm) { 
-             ret += (char)(x1 + 'a'); 
-             ret += (char)(y1 + '1'); 
-             ret += isCapture(pos, move) ? 'x' : '-'; 
-         } else { 
-             if (p == (pos.isWhiteMove() ? Piece::WPAWN : Piece::BPAWN)) { 
-                 if (isCapture(pos, move)) 
-                     ret += (char)(x1 + 'a'); 
-             } else { 
-                 int numSameTarget = 0; 
-                 int numSameFile = 0; 
-                 int numSameRow = 0; 
-                 for (int mi = 0; mi < moves.size; mi++) { 
-                     const Move& m = moves[mi]; 
-                     if (m.isEmpty()) 
-                         break; 
-                     if ((pos.getPiece(m.from()) == p) && (m.to() == move.to())) { 
-                         numSameTarget++; 
-                         if (Position::getX(m.from()) == x1) 
-                             numSameFile++; 
-                         if (Position::getY(m.from()) == y1) 
-                             numSameRow++; 
-                     } 
-                 } 
-                 if (numSameTarget < 2) { 
-                     // No file/row info needed 
-                 } else if (numSameFile < 2) { 
-                     ret += (char)(x1 + 'a');   // Only file info needed 
-                 } else if (numSameRow < 2) { 
-                     ret += (char)(y1 + '1');   // Only row info needed 
-                 } else { 
-                     ret += (char) (x1 + 'a');   // File and row info needed 
-                     ret += (char) (y1 + '1'); 
-                 } 
-             } 
-             if (isCapture(pos, move)) 
-                 ret += 'x'; 
-         } 
-         ret += (char)(x2 + 'a'); 
-         ret += (char)(y2 + '1'); 
-         if (move.promoteTo() != Piece::EMPTY) 
-             ret += pieceToChar(move.promoteTo()); 
-     } 
-     UndoInfo ui; 
-     if (MoveGen::givesCheck(pos, move)) { 
-         pos.makeMove(move, ui); 
-         MoveList nextMoves; 
-         MoveGen::pseudoLegalMoves(pos, nextMoves); 
-         MoveGen::removeIllegal(pos, nextMoves); 
-         if (nextMoves.size == 0) 
-             ret += '#'; 
-         else 
-             ret += '+'; 
-         pos.unMakeMove(move, ui); 
-     } 
-   
-     return ret; 
- } 
-   
- std::string 
- TextIO::moveToString(const Position& pos, const Move& move, bool longForm) { 
-     MoveList moves; 
-     MoveGen::pseudoLegalMoves(pos, moves); 
-     Position tmpPos(pos); 
-     MoveGen::removeIllegal(tmpPos, moves); 
-     return ::moveToString(tmpPos, move, longForm, moves); 
- } 
-   
- namespace { 
-     struct MoveInfo { 
-         int piece = -1;             // -1 for unspecified 
-         int fromX = -1, fromY = -1; // -1 for unspecified 
-         int toX = -1, toY = -1;     // -1 for unspecified 
-         int promPiece = -1;         // -1 for unspecified 
-     }; 
- } 
-   
- Move 
- TextIO::stringToMove(Position& pos, const std::string& strMoveIn) { 
-     std::string strMove; 
-     for (size_t i = 0; i < strMoveIn.length(); i++) { 
-         switch (strMoveIn[i]) { 
-         case '=': 
-         case '+': 
-         case '#': 
-             break; 
-         default: 
-             strMove += strMoveIn[i]; 
-             break; 
-         } 
-     } 
-   
-     Move move; 
-     if (strMove == "--") 
-         return move; 
-   
-     const bool wtm = pos.isWhiteMove(); 
-   
-     MoveInfo info; 
-     bool capture = false; 
-     if ((strMove == "O-O") || (strMove =="0-0") || (strMove == "o-o")) { 
-         info.piece = wtm ? Piece::WKING : Piece::BKING; 
-         info.fromX = 4; 
-         info.toX = 6; 
-         info.fromY = info.toY = wtm ? 0 : 7; 
-         info.promPiece = Piece::EMPTY; 
-     } else if ((strMove == "O-O-O") || (strMove == "0-0-0") || (strMove == "o-o-o")) { 
-         info.piece = wtm ? Piece::WKING : Piece::BKING; 
-         info.fromX = 4; 
-         info.toX = 2; 
-         info.fromY = info.toY = wtm ? 0 : 7; 
-         info.promPiece = Piece::EMPTY; 
-     } else { 
-         bool atToSq = false; 
-         for (size_t i = 0; i < strMove.length(); i++) { 
-             char c = strMove[i]; 
-             if (i == 0) { 
-                 int piece = charToPiece(wtm, c); 
-                 if (piece >= 0) { 
-                     info.piece = piece; 
-                     continue; 
-                 } 
-             } 
-             int tmpX = c - 'a'; 
-             if ((tmpX >= 0) && (tmpX < 8)) { 
-                 if (atToSq || (info.fromX >= 0)) 
-                     info.toX = tmpX; 
-                 else 
-                     info.fromX = tmpX; 
-             } 
-             int tmpY = c - '1'; 
-             if ((tmpY >= 0) && (tmpY < 8)) { 
-                 if (atToSq || (info.fromY >= 0)) 
-                     info.toY = tmpY; 
-                 else 
-                     info.fromY = tmpY; 
-             } 
-             if ((c == 'x') || (c == '-')) { 
-                 atToSq = true; 
-                 if (c == 'x') 
-                     capture = true; 
-             } 
-             if (i == strMove.length() - 1) { 
-                 int promPiece = charToPiece(wtm, c); 
-                 if (promPiece >= 0) { 
-                     info.promPiece = promPiece; 
-                 } 
-             } 
-         } 
-         if ((info.fromX >= 0) && (info.toX < 0)) { 
-             info.toX = info.fromX; 
-             info.fromX = -1; 
-         } 
-         if ((info.fromY >= 0) && (info.toY < 0)) { 
-             info.toY = info.fromY; 
-             info.fromY = -1; 
-         } 
-         if (info.piece < 0) { 
-             bool haveAll = (info.fromX >= 0) && (info.fromY >= 0) && 
-                            (info.toX >= 0) && (info.toY >= 0); 
-             if (!haveAll) 
-                 info.piece = wtm ? Piece::WPAWN : Piece::BPAWN; 
-         } 
-         if (info.promPiece < 0) 
-             info.promPiece = Piece::EMPTY; 
-     } 
-   
-     MoveList moves; 
-     MoveGen::pseudoLegalMoves(pos, moves); 
-     MoveGen::removeIllegal(pos, moves); 
-   
-     std::vector<Move> matches; 
-     for (int i = 0; i < moves.size; i++) { 
-         const Move& m = moves[i]; 
-         int p = pos.getPiece(m.from()); 
-         bool match = true; 
-         if ((info.piece >= 0) && (info.piece != p)) 
-             match = false; 
-         if ((info.fromX >= 0) && (info.fromX != Position::getX(m.from()))) 
-             match = false; 
-         if ((info.fromY >= 0) && (info.fromY != Position::getY(m.from()))) 
-             match = false; 
-         if ((info.toX >= 0) && (info.toX != Position::getX(m.to()))) 
-             match = false; 
-         if ((info.toY >= 0) && (info.toY != Position::getY(m.to()))) 
-             match = false; 
-         if ((info.promPiece >= 0) && (info.promPiece != m.promoteTo())) 
-             match = false; 
-         if (match) 
-             matches.push_back(m); 
-     } 
-     int nMatches = matches.size(); 
-     if (nMatches == 0) 
-         return move; 
-     else if (nMatches == 1) 
-         return matches[0]; 
-     if (!capture) 
-         return move; 
-     for (size_t i = 0; i < matches.size(); i++) { 
-         const Move& m = matches[i]; 
-         int capt = pos.getPiece(m.to()); 
-         if (capt != Piece::EMPTY) { 
-             if (move.isEmpty()) { 
-                 move = m; 
-             } else { 
-                 move = Move(); 
-                 return move; 
-             } 
-         } 
-     } 
-     return move; 
- } 
-   
- std::string 
- TextIO::asciiBoard(const Position& pos) { 
-     std::string ret; 
-     ret += "    +----+----+----+----+----+----+----+----+\n"; 
-     for (int y = 7; y >= 0; y--) { 
-         ret += "    |"; 
-         for (int x = 0; x < 8; x++) { 
-             ret += ' '; 
-             int p = pos.getPiece(Position::getSquare(x, y)); 
-             if (p == Piece::EMPTY) { 
-                 bool dark = Position::darkSquare(x, y); 
-                 ret.append(dark ? ".. |" : "   |"); 
-             } else { 
-                 ret += Piece::isWhite(p) ? ' ' : '*'; 
-                 std::string pieceName = pieceToChar(p); 
-                 if (pieceName.length() == 0) 
-                     pieceName = "P"; 
-   
-                 ret += pieceName; 
-                 ret += " |"; 
-             } 
-         } 
-   
-         ret += ("\n    +----+----+----+----+----+----+----+----+\n"); 
-     } 
-   
-     return ret; 
- } 
-   
- std::string 
- TextIO::asciiBoard(U64 mask) { 
-     std::string ret; 
-     for (int y = 7; y >= 0; y--) { 
-         for (int x = 0; x < 8; x++) { 
-             int sq = Position::getSquare(x, y); 
-             ret += (mask & (1ULL << sq)) ? '1' : '0'; 
-         } 
-         ret += '\n'; 
-     } 
-     return ret; 
- } 
-