<< std::setfill(' ') << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; )
- os << UCI::square(pop_lsb(&b)) << " ";
+ os << UCI::square(pos, pop_lsb(&b)) << " ";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
/// This function is not very robust - make sure that input FENs are correct,
/// this is assumed to be the responsibility of the GUI.
-Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
+Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, StateInfo* si, Thread* th, bool sfen) {
/*
A FEN string defines a particular position using only the ASCII character set.
// 2. Active color
ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK);
+ // Invert side to move for SFEN
+ if (sfen)
+ sideToMove = ~sideToMove;
ss >> token;
// 3-4. Skip parsing castling and en passant flags if not present
st->epSquare = SQ_NONE;
- if (!isdigit(ss.peek()))
+ if (!isdigit(ss.peek()) && !sfen)
{
- // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
- // Shredder-FEN that uses the letters of the columns on which the rooks began
- // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
- // if an inner rook is associated with the castling right, the castling tag is
- // replaced by the file letter of the involved rook, as for the Shredder-FEN.
- while ((ss >> token) && !isspace(token))
- {
- Square rsq;
- Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, ROOK);
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
+ // if an inner rook is associated with the castling right, the castling tag is
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
+ while ((ss >> token) && !isspace(token))
+ {
+ Square rsq;
+ Color c = islower(token) ? BLACK : WHITE;
+ Piece rook = make_piece(c, ROOK);
- token = char(toupper(token));
+ token = char(toupper(token));
- if (token == 'K')
- for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
+ if (token == 'K')
+ for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
- else if (token == 'Q')
- for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
+ else if (token == 'Q')
+ for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
- else if (token >= 'A' && token <= 'H')
- rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
+ else if (token >= 'A' && token <= 'H')
+ rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
- else
- continue;
+ else
+ continue;
- set_castling_right(c, rsq);
- }
+ set_castling_right(c, rsq);
+ }
- // 4. En passant square. Ignore if no pawn capture is possible
- if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == '3' || row == '6')))
- {
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ // 4. En passant square. Ignore if no pawn capture is possible
+ if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
+ && ((ss >> row) && (row == '3' || row == '6')))
+ {
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
- if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
- || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
- st->epSquare = SQ_NONE;
- }
+ if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
+ || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
+ st->epSquare = SQ_NONE;
+ }
}
// Check counter for nCheck
- ss >> std::skipws >> token;
+ ss >> std::skipws >> token >> std::noskipws;
if (max_check_count() && ss.peek() == '+')
{
ss.putback(token);
// 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> gamePly;
+ if (sfen)
+ {
+ // Pieces in hand for SFEN
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == '-')
+ continue;
+ else if ((idx = piece_to_char().find(token)) != string::npos)
+ add_to_hand(color_of(Piece(idx)), type_of(Piece(idx)));
+ }
+ // Move count is in ply for SFEN
+ ss >> std::skipws >> gamePly;
+ gamePly = std::max(gamePly - 1, 0);
+ }
+ else
+ {
+ ss >> std::skipws >> st->rule50 >> gamePly;
- // Convert from fullmove starting from 1 to gamePly starting from 0,
- // handle also common incorrect FEN with fullmove = 0.
- gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
+ // handle also common incorrect FEN with fullmove = 0.
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ }
chess960 = isChess960;
thisThread = th;
if (max_check_count())
ss << " " << (max_check_count() - st->checksGiven[WHITE]) << "+" << (max_check_count() - st->checksGiven[BLACK]);
- ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
+ ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ")
<< st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
return ss.str();
string token, fen;
is >> token;
+ // Parse as SFEN if specified
+ bool sfen = token == "sfen";
if (token == "startpos")
{
fen = variants.find(Options["UCI_Variant"])->second->startFen;
is >> token; // Consume "moves" token if any
}
- else if (token == "fen")
+ else if (token == "fen" || token == "sfen")
while (is >> token && token != "moves")
fen += token + " ";
else
return;
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
- pos.set(variants.find(Options["UCI_Variant"])->second, fen, Options["UCI_Chess960"], &states->back(), Threads.main());
+ pos.set(variants.find(Options["UCI_Variant"])->second, fen, Options["UCI_Chess960"], &states->back(), Threads.main(), sfen);
// Parse move list (if any)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
else if (token == "ponderhit")
Threads.ponder = false; // Switch to normal search
- else if (token == "uci")
+ else if (token == "uci" || token == "usi")
+ {
+ Options["Protocol"] = token;
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
- << "\nuciok" << sync_endl;
+ << "\n" << token << "ok" << sync_endl;
+ }
else if (token == "setoption") setoption(is);
else if (token == "go") go(pos, is, states);
else if (token == "position") position(pos, is, states);
- else if (token == "ucinewgame") Search::clear();
+ else if (token == "ucinewgame" || token == "usinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl;
// Additional custom non-UCI commands, mainly for debugging
if (abs(v) < VALUE_MATE - MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
+ else if (Options["Protocol"] == "usi")
+ // In USI, mate distance is given in ply
+ ss << "mate " << (v > 0 ? VALUE_MATE - v : -VALUE_MATE - v);
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v - 1) / 2;
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
-std::string UCI::square(Square s) {
- return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
+std::string UCI::square(const Position& pos, Square s) {
+ return Options["Protocol"] == "usi" ? std::string{ char('1' + pos.max_file() - file_of(s)), char('a' + pos.max_rank() - rank_of(s)) }
+ : std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
}
if (type_of(m) == CASTLING && !pos.is_chess960())
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
- string move = (type_of(m) == DROP ? std::string{pos.piece_to_char()[type_of(pos.moved_piece(m))], '@'}
- : UCI::square(from)) + UCI::square(to);
+ string move = (type_of(m) == DROP ? std::string{pos.piece_to_char()[type_of(pos.moved_piece(m))],
+ Options["Protocol"] == "usi" ? '*' : '@'}
+ : UCI::square(pos, from)) + UCI::square(pos, to);
if (type_of(m) == PROMOTION)
move += pos.piece_to_char()[make_piece(BLACK, promotion_type(m))];