FEN (Forsyth-Edwards Notation) is a standard notation for describing chess positions. The chessops library provides functions to parse and generate FEN strings.
Constants
The library exports several standard FEN constants:
import { INITIAL_FEN, INITIAL_EPD, INITIAL_BOARD_FEN, EMPTY_FEN } from 'chessops/fen';
console.log(INITIAL_FEN);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
console.log(INITIAL_BOARD_FEN);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'
console.log(INITIAL_EPD);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -'
console.log(EMPTY_FEN);
// '8/8/8/8/8/8/8/8 w - - 0 1'
Parsing FEN
parseFen
Parses a complete FEN string into a Setup object. Returns a Result type for safe error handling.
import { parseFen } from 'chessops/fen';
const result = parseFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
if (result.isOk) {
const setup = result.value;
console.log(setup.turn); // 'white'
console.log(setup.halfmoves); // 0
console.log(setup.fullmoves); // 1
} else {
console.error(result.error); // FenError
}
The parseFen function accepts partial FEN strings. Missing parts will use defaults (white to move, no castling rights, etc.).
Setup Object
The parsed setup contains:
board - Board with piece positions
turn - Side to move ('white' or 'black')
castlingRights - SquareSet of rook squares with castling rights
epSquare - En passant target square (if any)
halfmoves - Halfmove clock for 50-move rule
fullmoves - Fullmove number
pockets - Crazyhouse pockets (if present)
remainingChecks - Three-check remaining checks (if present)
parseBoardFen
Parses only the board part of a FEN string:
import { parseBoardFen } from 'chessops/fen';
const result = parseBoardFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR');
if (result.isOk) {
const board = result.value;
const piece = board.get(0); // Square a1
console.log(piece); // { role: 'rook', color: 'white' }
}
parsePockets
Parses Crazyhouse pocket notation:
import { parsePockets } from 'chessops/fen';
const result = parsePockets('QNBRPpnbrq');
if (result.isOk) {
const pockets = result.value;
console.log(pockets.white.queen); // 1
console.log(pockets.black.pawn); // 1
}
parseCastlingFen
Parses castling rights from FEN notation:
import { parseCastlingFen } from 'chessops/fen';
import { Board } from 'chessops/board';
const board = Board.default();
const result = parseCastlingFen(board, 'KQkq');
if (result.isOk) {
const castlingRights = result.value;
console.log(castlingRights.has(0)); // true (a1 rook)
console.log(castlingRights.has(7)); // true (h1 rook)
}
Supports both standard notation (KQkq) and Chess960 notation (file letters like HAha).
parseRemainingChecks
Parses remaining checks for Three-check variant:
import { parseRemainingChecks } from 'chessops/fen';
const result = parseRemainingChecks('3+2');
if (result.isOk) {
const checks = result.value;
console.log(checks.white); // 3
console.log(checks.black); // 2
}
Writing FEN
makeFen
Converts a Setup object to a complete FEN string:
import { makeFen } from 'chessops/fen';
import { defaultSetup } from 'chessops/setup';
const setup = defaultSetup();
const fen = makeFen(setup);
console.log(fen);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
Options
You can generate EPD (without move counters) instead:
import { makeFen } from 'chessops/fen';
import { defaultSetup } from 'chessops/setup';
const setup = defaultSetup();
const epd = makeFen(setup, { epd: true });
console.log(epd);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -'
makeBoardFen
Generates only the board part:
import { makeBoardFen } from 'chessops/fen';
import { Board } from 'chessops/board';
const board = Board.default();
const boardFen = makeBoardFen(board);
console.log(boardFen);
// 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'
makePockets
Generates Crazyhouse pocket notation:
import { makePockets } from 'chessops/fen';
import { Material } from 'chessops/setup';
const pockets = Material.empty();
pockets.white.queen = 1;
pockets.black.pawn = 2;
const pocketStr = makePockets(pockets);
console.log(pocketStr); // 'Qpp'
makeCastlingFen
Generates castling rights notation:
import { makeCastlingFen } from 'chessops/fen';
import { Board } from 'chessops/board';
import { SquareSet } from 'chessops/squareSet';
const board = Board.default();
const castlingRights = SquareSet.corners();
const castlingFen = makeCastlingFen(board, castlingRights);
console.log(castlingFen); // 'KQkq'
makeRemainingChecks
Generates remaining checks notation:
import { makeRemainingChecks } from 'chessops/fen';
import { RemainingChecks } from 'chessops/setup';
const checks = new RemainingChecks(3, 2);
const checksStr = makeRemainingChecks(checks);
console.log(checksStr); // '3+2'
Piece Parsing
parsePiece
Parses a piece from FEN notation:
import { parsePiece } from 'chessops/fen';
const piece = parsePiece('K');
console.log(piece); // { role: 'king', color: 'white' }
const promotedPiece = parsePiece('q~');
console.log(promotedPiece); // { role: 'queen', color: 'black', promoted: true }
makePiece
Converts a piece to FEN notation:
import { makePiece } from 'chessops/fen';
const str = makePiece({ role: 'knight', color: 'white' });
console.log(str); // 'N'
const promotedStr = makePiece({ role: 'queen', color: 'black', promoted: true });
console.log(promotedStr); // 'q~'
Error Handling
All parsing functions return a Result type from @badrap/result. Use .isOk, .isErr, .unwrap(), or .unwrap(onOk, onErr) to handle errors:
import { parseFen } from 'chessops/fen';
const result = parseFen('invalid fen');
if (result.isErr) {
const error = result.error;
console.log(error.message); // InvalidFen enum value
}
// Or use unwrap with error handler
const setup = parseFen(fen).unwrap(
setup => setup,
error => {
console.error('Invalid FEN:', error.message);
return defaultSetup();
}
);
FenError Enum
Possible error values:
enum InvalidFen {
Fen = 'ERR_FEN',
Board = 'ERR_BOARD',
Pockets = 'ERR_POCKETS',
Turn = 'ERR_TURN',
Castling = 'ERR_CASTLING',
EpSquare = 'ERR_EP_SQUARE',
RemainingChecks = 'ERR_REMAINING_CHECKS',
Halfmoves = 'ERR_HALFMOVES',
Fullmoves = 'ERR_FULLMOVES',
}
Complete Example
Parsing and modifying a position:
import { parseFen, makeFen } from 'chessops/fen';
import { Chess } from 'chessops/chess';
import { parseUci } from 'chessops';
// Parse FEN
const result = parseFen('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1');
if (result.isOk) {
const setup = result.value;
// Create position
const pos = Chess.fromSetup(setup).unwrap();
// Make a move
const move = parseUci('e7e5');
if (move) {
pos.play(move);
}
// Generate new FEN
const newFen = makeFen(pos.toSetup());
console.log(newFen);
// 'rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2'
}