SkillAgentSearch skills...

Chessalyzer.js

A JavaScript library for batch analyzing chess games

Install / Use

/learn @yschroe/Chessalyzer.js
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<img src="https://i.imgur.com/X7Q2xIx.png" style="height: 150px">

A JavaScript library for batch analyzing chess games.

npm version

Index

Features

  • Batch process PGN files and track statistics of your games
  • Filter games (e.g. only analyze games where WhiteElo > 1800)
  • Fully modular, track only the stats you need to preserve performance
  • Generate heatmaps out of the generated data
  • It's fast and highly parallelized: Processes >10M moves/s on an Apple M1, >4.900.000 moves/s on a Ryzen 5 2600X (PGN parsing only)
  • Handles big files easily
  • Just one dependency (chalk)

Installation

  1. Install package
npm install chessalyzer.js
  1. Import the Chessalyzer object (Note: since V2.0 chessalyzer is an ES module so you can't use the Node.js require(...) syntax).
import { Chessalyzer } from 'chessalyzer.js';
  1. Use the library. See next chapters for examples.
Chessalyzer.analyzePGN('<pathToPgnFile', yourAnalysisConfig);
  1. Check out the examples or the docs.

How it works

Chessalyzer.js consists of two submodules which work hand-in-hand: The first module is the Chessalyzer class which handles the PGN parsing and provides you with a function for previewing heatmaps. The class by itself is static (meaning it can not be instantiated and does not store data in itself) and does not track any statistics though. For this you need a Tracker object which you then can pass into the parsing function of the Chessalyzer class. The Chessalyzer class recognizes the Tracker objects and passes data into it. Typically this will either be an Action[] containing information about e.g. which piece moved from where to where or which piece captured which other piece for each move of each game. Additionally you can also use the information from the header of the PGN file, where you can find e.g. the player names and which opening was played (ECO code).

Inside the Tracker object you can do whatever you want with the data. If you want to track some obscure stat like how often the e pawn was promoted to a rook on the a8 square you could write a Tracker for that. Chessalyzer.js ships with three different preconfigured Trackers which should cover most usecases, so if you are happy with that you don't need to code your own Tracker.

Lastly chessalyzer.js provides you with functions to convert your raw data from your Trackers into heatmaps which you then can use in your frontend of choice.

Examples

Basic Usage

Let's start with a basic example. Here we simply want to track the tile occupation (=how often did each tile have a piece on it) for the whole board. For this we can use the preconfigured TileTracker class from the library. Afterwards we want to create a heatmap out of the data to visualize the tile occupation. For this basic heatmap a preset is also provided:

// Import the library and trackers.
import { Chessalyzer, TileTracker } from 'chessalyzer.js';

// Create basic tile tracker.
const tileTracker = new TileTracker();

// Start a batch analysis for the PGN file at <pathToPgnFile>.
// The data is tracked directly inside the tileTracker instance.
// See later chapters for how the data is structured inside the trackers.
await Chessalyzer.analyzePGN('<pathToPgnFile>', { trackers: [tileTracker] });

// Generate a tile occupation heatmap.
const heatmapData = tileTracker.generateHeatmap('TILE_OCC_ALL');

// Print heatmap to console for preview.
Chessalyzer.printHeatmap(heatmapData);

// ...or use heatmapData with your favourite frontend.

Filtering

You can also filter the PGN file for specific criteria, e.g. only evaluate games where WhiteElo > 2000:

// Create filter function that returns true for all games where WhiteElo > 2000.
// The 'game' object passed contains every header key included in the pgn file (case sensitive).
let fil = function (game) {
    return game.WhiteElo > 2000;
};

await Chessalyzer.analyzePGN('<pathToPgnFile>', {
    trackers: [tileTracker],
    config: {
        filter: fil
    }
});

// ...do something with the tileTracker data.

Compare Analyses

You can also generate a comparison heat map where you can compare the data of two different analyses. Let's say you wanted to compare how the white player occupates the board between a lower rated player and a higher rated player. To get comparable results 1000 games of each shall be evaluated:

// Create two Tile Trackers.
const tileT1 = new TileTracker();
const tileT2 = new TileTracker();

// Start the analysis.
// Instead of passing just one analysis config you can also pass an array of configs,
// tileT1 will only receive games with WhiteElo >2000, tileT2 only receives games with WhiteElo < 1200.
await Chessalyzer.analyzePGN('<pathToPgnFile>', [
    {
        trackers: [tileT1],
        config: {
            filter: (game) => game.WhiteElo > 2000,
            cntGames: 1000
        }
    },
    {
        trackers: [tileT2],
        config: {
            filter: (game) => game.WhiteElo < 1200,
            cntGames: 1000
        }
    }
]);

// Create an evaluation function for the heat map.
let func = (data, loopSqrData) => {
    const { coords } = loopSqrData;
    let val = data.tiles[coords[0]][coords[1]].w.wasOn;
    val = (val * 100) / data.cntMovesTotal;
    return val;
};

// Generate the comparison heatmap.
const heatmapData = tileT1.generateComparisonHeatmap(tileT2, func);

// Use heatmapData.

Multithreaded analysis

Per default chessalyzer.js uses Node.js Worker Threads to read-in the pgn file and analyze the data in parallel.

// Start a multithreaded batch analysis for the PGN file at <pathToPgnFile>.

await Chessalyzer.analyzePGN(
    '<pathToPgnFile>',
    {
        trackers: [tileTracker],
        config: {
            cntGames: 10000
        }
    },
    { batchSize: 500 }
);

// ...

analyzePGN(...) in multithreaded mode reads in chunks of games of size batchSize and starts the analysis of this chunk in a different thread. While the other thread parses and analyzes the games, the next chunk is read-in from the PGN file in the main thread in parallel. Every time the defined count of games has been read in, chessalyzer.js checks if any of the previously started threads is ready to analyze new data. If no free thread is found a new thread is started.

Forcing single threaded mode

To use singlethreaded mode in which the games are read in and analyzed sequentially on a single thread, simply pass null as the 3rd argument into analyzePGN(...).

Important

To use a custom tracker with your multithreaded analysis please see the important notes at the Custom Trackers section.

Heatmap generation functions

The function you create for heatmap generation gets passed up to four parameters (inside generateHeatMap(...)):

  1. data: The data that is the basis for the heatmap. Per default this data is the Tracker you called the generateHeatMap(...) function from itself.

  2. loopSqrData: Contains informations about the square the current heatmap value shall be calculated for. The generateHeatMap(...) function loops over every square of the board to calculate a heat map value for each tile. sqrData is an object with the following entries:

    interface SquareData {
        // The square in algebraic notation (e.g. 'a2').
        alg: string;
    
        // The square in board coordinates (e.g. [6,0]).
        coords: number[];
    
        // The piece that starts at the passed square. If no piece starts at the passed square, piece is null.
        piece: {
            // Name of the piece (e.g. 'Pa' for the a-pawn).
            color: string;
            // Color of the piece ('b' or 'w').
            name: string;
        };
    }
    
  3. sqrData: Contains informations about the square you passed into the generateHeatMap() function. The structure of sqrData is the same as of loopSqrData. You'll need this for extracting the values for the square / piece you are interested in. For example if you want to generate a heatmap for white's a pawn, the square for sqrData would be 'a2' (= starting position of the white a pawn).

  4. optData: Optional data you may need in this function. For example, if you wanted to generate a heatmap to show the average position of a piece after X moves, you could pass that 'X' here.

Tracked statistics

Built-in

chessalyzer.js comes with three built-in trackers, which can be directly imported into your script:

GameTracker:

  • result An object which counts the results of the tracked games. Contains the keys white, draw and black

  • ECO Counts the ECO keys of the processed games. ECO is an object that contains the different keys, for example 'A00'.

  • cntGames
    Number of games processed

PieceTracker:

  • b
    Blacks pieces. Tracks how often a specific black piece took a specific white piece. E.g. b.Pa.Qd tracks how often the black a-pawn took whites queen.

  • w
    Same for whites pieces.

TileTracker:

  • tiles[][]
    Represen
View on GitHub
GitHub Stars16
CategoryDevelopment
Updated4mo ago
Forks2

Languages

TypeScript

Security Score

92/100

Audited on Nov 27, 2025

No findings