Module pgn

Parse, transform and write PGN.

The parser will interpret any input as a PGN, creating a tree of syntactically valid (but not necessarily legal) moves, skipping any invalid tokens.

import { parsePgn, startingPosition } from 'chessops/pgn';
import { parseSan } from 'chessops/san';

const pgn = '1. d4 d5 *';
const games = parsePgn(pgn);
for (const game of games) {
const pos = startingPosition(game.headers).unwrap();
for (const node of game.moves.mainline()) {
const move = parseSan(pos, node.san);
if (!move) break; // Illegal move
pos.play(move);
}
}

The module also provides a denial-of-service resistant streaming parser. It can be configured with a budget for reasonable complexity of a single game, fed with chunks of text, and will yield parsed games as they are completed.


import { createReadStream } from 'fs';
import { PgnParser } from 'chessops/pgn';

const stream = createReadStream('games.pgn', { encoding: 'utf-8' });

const parser = new PgnParser((game, err) => {
if (err) {
// Budget exceeded.
stream.destroy(err);
}

// Use game ...
});

await new Promise<void>(resolve =>
stream
.on('data', (chunk: string) => parser.parse(chunk, { stream: true }))
.on('close', () => {
parser.parse('');
resolve();
})
);

You can use walk to visit all nodes in the game tree, or transform to augment it with user data.

Both allow you to provide context. You update the context inside the callback, and it is automatically clone()-ed at each fork. In the example below, the current position pos is provided as context.

import { transform } from 'chessops/pgn';
import { makeFen } from 'chessops/fen';
import { parseSan, makeSanAndPlay } from 'chessops/san';

const pos = startingPosition(game.headers).unwrap();
game.moves = transform(game.moves, pos, (pos, node) => {
const move = parseSan(pos, node.san);
if (!move) {
// Illegal move. Returning undefined cuts off the tree here.
return;
}

const san = makeSanAndPlay(pos, move); // Mutating pos!

return {
...node, // Keep comments and annotation glyphs
san, // Normalized SAN
fen: makeFen(pos.toSetup()), // Add arbitrary user data to node
};
});

Requires each node to at least have a san property.

import { makePgn } from 'chessops/pgn';

const rewrittenPgn = makePgn(game);

Index

Classes

Interfaces

Type Aliases

Functions