
chessPGN is a comprehensive TypeScript chess library for chess move generation, validation, and PGN parsing. Based on a fork of chess.js, it extends the original with powerful multi-game PGN file parsing capabilities while maintaining full backward compatibility.
ChessPGN) and modern (Game) interfacesnpm install @chess-pgn/chess-pgn
import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
// Make some moves
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
chess.move('Nc6')
// Get current position
console.log(chess.fen())
// rnbqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3
// Check game state
console.log(chess.isCheck()) // false
console.log(chess.isCheckmate()) // false
// Get legal moves
console.log(chess.moves())
// ['a3', 'a4', 'b3', 'b4', 'c3', 'c4', 'd3', 'd4', ...]
// Export as PGN
console.log(chess.pgn())
// 1. e4 e5 2. Nf3 Nc6
import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
while (!chess.isGameOver()) {
const moves = chess.moves()
const move = moves[Math.floor(Math.random() * moves.length)]
chess.move(move)
}
console.log(chess.pgn())
import { ChessPGN } from '@chess-pgn/chess-pgn'
const pgn = `[Event "Casual Game"]
[Site "New York"]
[Date "2025.01.15"]
[White "Alice"]
[Black "Bob"]
[Result "1-0"]
1. e4 e5 2. Nf3 Nc6 3. Bb5 1-0`
const chess = new ChessPGN()
chess.loadPgn(pgn)
console.log(chess.getHeaders())
// { Event: 'Casual Game', Site: 'New York', ... }
console.log(chess.history())
// ['e4', 'e5', 'Nf3', 'Nc6', 'Bb5']
Efficiently parse large PGN files with multiple games:
import { indexPgnGames } from '@chess-pgn/chess-pgn'
import * as fs from 'fs'
const pgnContent = fs.readFileSync('games.pgn', 'utf8')
// Create cursor with worker threads for parallel parsing
const cursor = indexPgnGames(pgnContent, {
workers: 4,
workerBatchSize: 10,
onError: (err, idx) => console.error(`Game ${idx}: ${err.message}`),
})
// Iterate through games
for await (const game of cursor) {
const headers = game.getHeaders()
console.log(`${headers.White} vs ${headers.Black}: ${headers.Result}`)
// Analyze final position
if (game.isCheckmate()) {
console.log('Checkmate!')
}
}
// Clean up worker threads
await cursor.terminate()
import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
// Check if square is attacked
console.log(chess.isAttacked('e5', 'w')) // true (knight attacks e5)
// Get all attackers of a square
console.log(chess.attackers('e5'))
// ['f3']
// Find pieces
console.log(chess.findPiece({ type: 'n', color: 'w' }))
// ['b1', 'f3']
// Get piece at square
console.log(chess.get('f3'))
// { type: 'n', color: 'w' }
import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.move('e5')
chess.move('Nf3')
// Get detailed move history
const history = chess.history({ verbose: true })
console.log(history)
/*
[
{
color: 'w',
from: 'e2',
to: 'e4',
piece: 'p',
san: 'e4',
before: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
after: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
},
...
]
*/
import { ChessPGN } from '@chess-pgn/chess-pgn'
const chess = new ChessPGN()
chess.move('e4')
chess.setComment('The most popular opening move')
chess.setSuffixAnnotation('!!') // Brilliant move
chess.move('e5')
chess.setComment('Symmetric response')
chess.setSuffixAnnotation('!') // Good move
// Get all comments
const comments = chess.getComments()
console.log(comments)
/*
[
{
fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1',
comment: 'The most popular opening move',
suffixAnnotation: '!!'
},
...
]
*/
// Export with comments
console.log(chess.pgn())
// 1. e4!! {The most popular opening move} e5! {Symmetric response}
ChessPGN - Legacy wrapper class with full chess functionalityGame - Modern core implementation (can be used directly)Move - Rich move object with detailed informationload(fen) - Load position from FEN notationreset() - Reset to starting positionclear() - Clear the boardfen() - Get FEN string of current positionmove(move) - Make a move (SAN string or object)moves() - Get all legal movesundo() - Undo last movehistory() - Get move historyisCheck() - Check if in checkisCheckmate() - Check if checkmateisStalemate() - Check if stalemateisDraw() - Check if drawn positionisGameOver() - Check if game is overturn() - Get side to moveget(square) - Get piece at squareboard() - Get 2D board arrayascii() - Get ASCII board representationattackers(square) - Get pieces attacking a squareisAttacked(square, color) - Check if square is attackedpgn() - Export game as PGN stringloadPgn(pgn) - Load game from PGNsetHeader(key, value) - Set PGN headergetHeaders() - Get all headerssetComment(comment) - Add comment to positiongetComment() - Get comment for positiongetComments() - Get all commentssetSuffixAnnotation(suffix) - Add move annotation (!!, !, ?, etc.)indexPgnGames(pgn, options) - Create cursor for multi-game PGN filesOptions:
workers - Enable parallel parsing with worker threadsworkerBatchSize - Games per batch (default: 10)strict - Strict PGN parsing modeonError - Error callback for malformed gameschessPGN uses a delegation pattern where ChessPGN wraps the core Game
class:
Game - Core chess logic, single source of truthChessPGN - Legacy wrapper, delegates to GameIChessGame - Common interface for both classesBoth classes implement the same interface and produce identical results, verified through extensive parity testing across 469 real games.
# Install dependencies
npm install
# Run tests
npm test
# Run all checks (format, lint, test, build)
npm run check
# Build the project
npm run build
# Format code
npm run format
chessPGN has comprehensive test coverage:
ChessPGN โก GameContributions are welcome! Please read our Contributing Guide to get started.
Ways to contribute:
This project is licensed under the BSD 2-Clause License - see the LICENSE file for details.