Setup.parseFen constructor
Setup.parseFen(
- String fen
Parses a Forsyth-Edwards-Notation string and returns a Setup.
The parser is relaxed:
- Supports X-FEN and Shredder-FEN for castling right notation.
- Accepts missing FEN fields (except the board) and fills them with
default values of
8/8/8/8/8/8/8/8 w - - 0 1. - Accepts multiple spaces and underscores (
_) as separators between FEN fields.
Throws a FenException if the provided FEN is not valid.
Implementation
factory Setup.parseFen(String fen) {
final parts = fen.split(RegExp(r'[\s_]+'));
if (parts.isEmpty) throw const FenException(IllegalFenCause.format);
// board and pockets
final boardPart = parts.removeAt(0);
Pockets? pockets;
Board board;
if (boardPart.endsWith(']')) {
final pocketStart = boardPart.indexOf('[');
if (pocketStart == -1) {
throw const FenException(IllegalFenCause.format);
}
board = Board.parseFen(boardPart.substring(0, pocketStart));
pockets = _parsePockets(
boardPart.substring(pocketStart + 1, boardPart.length - 1));
} else {
final pocketStart = _nthIndexOf(boardPart, '/', 7);
if (pocketStart == -1) {
board = Board.parseFen(boardPart);
} else {
board = Board.parseFen(boardPart.substring(0, pocketStart));
pockets = _parsePockets(boardPart.substring(pocketStart + 1));
}
}
// turn
Side turn;
if (parts.isEmpty) {
turn = Side.white;
} else {
final turnPart = parts.removeAt(0);
if (turnPart == 'w') {
turn = Side.white;
} else if (turnPart == 'b') {
turn = Side.black;
} else {
throw const FenException(IllegalFenCause.turn);
}
}
// Castling
SquareSet castlingRights;
if (parts.isEmpty) {
castlingRights = SquareSet.empty;
} else {
final castlingPart = parts.removeAt(0);
castlingRights = _parseCastlingFen(board, castlingPart);
}
// En passant square
Square? epSquare;
if (parts.isNotEmpty) {
final epPart = parts.removeAt(0);
if (epPart != '-') {
epSquare = Square.parse(epPart);
if (epSquare == null) {
throw const FenException(IllegalFenCause.enPassant);
}
}
}
// move counters or remainingChecks
String? halfmovePart = parts.isNotEmpty ? parts.removeAt(0) : null;
(int, int)? earlyRemainingChecks;
if (halfmovePart != null && halfmovePart.contains('+')) {
earlyRemainingChecks = _parseRemainingChecks(halfmovePart);
halfmovePart = parts.isNotEmpty ? parts.removeAt(0) : null;
}
final halfmoves = halfmovePart != null ? _parseSmallUint(halfmovePart) : 0;
if (halfmoves == null) {
throw const FenException(IllegalFenCause.halfmoveClock);
}
final fullmovesPart = parts.isNotEmpty ? parts.removeAt(0) : null;
final fullmoves =
fullmovesPart != null ? _parseSmallUint(fullmovesPart) : 1;
if (fullmoves == null) {
throw const FenException(IllegalFenCause.fullmoveNumber);
}
final remainingChecksPart = parts.isNotEmpty ? parts.removeAt(0) : null;
(int, int)? remainingChecks;
if (remainingChecksPart != null) {
if (earlyRemainingChecks != null) {
throw const FenException(IllegalFenCause.remainingChecks);
}
remainingChecks = _parseRemainingChecks(remainingChecksPart);
} else if (earlyRemainingChecks != null) {
remainingChecks = earlyRemainingChecks;
}
if (parts.isNotEmpty) {
throw const FenException(IllegalFenCause.format);
}
return Setup(
board: board,
pockets: pockets,
turn: turn,
castlingRights: castlingRights,
epSquare: epSquare,
halfmoves: halfmoves,
fullmoves: fullmoves,
remainingChecks: remainingChecks,
);
}