Skip to content

Complete TypeScript chess engine with zero dependencies for Node.js >=24 and browsers. Features configurable AI (5 predefined difficulty levels), stateful/stateless APIs, and supports JSON and FEN formats.

License

Notifications You must be signed in to change notification settings

josefjadrny/js-chess-engine

Repository files navigation

JS-CHESS-ENGINE

GitHub package.json version License: MIT

Complete TypeScript chess engine without dependencies for Node.js >=24 and browsers. Includes configurable AI with difficulty levels 1-5.

⚠️ Upgrading from v1? See Breaking Changes section at the end of this document

Install

Install with npm

npm i js-chess-engine --save

or install with yarn

yarn add js-chess-engine

Node.js Requirement: Node.js >=24 is required for v2.

Import

TypeScript / ESM

// Import Game class and stateless functions
import { Game, moves, status, move, aiMove, getFen } from 'js-chess-engine'

// Import types for TypeScript
import type { BoardConfig, PieceSymbol, MovesMap, AILevel } from 'js-chess-engine'

const game = new Game()

CommonJS (Node.js)

const { Game, moves, status, move, aiMove, getFen } = require('js-chess-engine')

const game = new Game()

React / Modern JavaScript

import { Game } from 'js-chess-engine'
import type { BoardConfig, MovesMap } from 'js-chess-engine'

const game = new Game()
const allMoves: MovesMap = game.moves()

Examples

js-chess-engine-app - React application example with js-chess-engine REST API backend (without persistent storage) - GitHub or LIVE DEMO

Note: Additional examples for v2 (server, console, AI match) will be added in a future release.

Documentation

You have two options for using this engine:

Option 1 - With in-memory

Use the Game class to create and manage a chess game. In this mode, the game state is cached in memory for better performance.

import { Game } from 'js-chess-engine'
import type { BoardConfig, MovesMap } from 'js-chess-engine'

const game = new Game()

You can export your game to JSON or FEN at any time and use these formats to restore your game later.

API Description

constructor

new Game(configuration) - Create a new game with optional initial configuration.

Params:

  • configuration BoardConfig | string (optional) - Chess board configuration (JSON object or FEN string). Default is standard starting position.
import { Game } from 'js-chess-engine'
import type { BoardConfig } from 'js-chess-engine'

// New game with starting position
const game1 = new Game()

// From FEN string
const game2 = new Game('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1')

// From BoardConfig object
const config: BoardConfig = { /* ... */ }
const game3 = new Game(config)

move

game.move(from, to) - Perform a move on the chessboard and recalculate game state. Returns full BoardConfig object (breaking change from v1).

Params:

  • from string (mandatory) - Starting square (case-insensitive, e.g., 'E2' or 'e2')
  • to string (mandatory) - Destination square (case-insensitive, e.g., 'E4' or 'e4')

Returns: BoardConfig - Full board configuration after the move

import type { BoardConfig } from 'js-chess-engine'

const newConfig: BoardConfig = game.move('E2', 'E4')
console.log(newConfig.pieces)  // {"E4": "P", "E1": "K", ...}

moves

game.moves(from?) - Get all legal moves for the current player. Optionally filter by a specific square.

Params:

  • from string (optional) - Square to filter moves from (case-insensitive, e.g., 'E2'). If omitted, returns moves for all pieces.

Returns: MovesMap - Object mapping from-squares to arrays of to-squares

import type { MovesMap } from 'js-chess-engine'

// Get all moves (no parameter)
const allMoves: MovesMap = game.moves()
// {"E2": ["E3", "E4"], "B1": ["A3", "C3"], ...}

// Get moves for specific square
const pawnMoves: MovesMap = game.moves('E2')
// {"E2": ["E3", "E4"]}

setPiece

game.setPiece(location, piece) - Add or replace a chess piece at the specified location.

Params:

  • location string (mandatory) - Square location (case-insensitive, e.g., 'E2')
  • piece PieceSymbol (mandatory) - Piece symbol using FEN notation (K, Q, R, B, N, P for white; k, q, r, b, n, p for black)
import type { PieceSymbol } from 'js-chess-engine'

const piece: PieceSymbol = 'Q'
game.setPiece('E5', piece)

removePiece

game.removePiece(location) - Remove a piece from the specified location.

Params:

  • location string (mandatory) - Square location (case-insensitive, e.g., 'E2')
game.removePiece('E5')

aiMove

⚠️ DEPRECATED: This method will be removed in v3.0.0. Use ai() instead, which returns both the move and board state.

game.aiMove(level) - Calculate and perform the best move for the current player using AI. Returns only the move (v1 API compatible).

Params:

  • level AILevel (optional) - AI difficulty level (1-5). See Computer AI section. Default: 3

Returns: HistoryEntry - The played move (e.g., {"E2": "E4"})

import type { HistoryEntry, AILevel } from 'js-chess-engine'

const level: AILevel = 4
const move: HistoryEntry = game.aiMove(level)
console.log(move) // {"E2": "E4"}

// To get board state after move, use exportJson()
const board = game.exportJson()

// RECOMMENDED: Use ai() instead
const result = game.ai({ level: 4 })
console.log(result.move)  // {"E2": "E4"}
console.log(result.board) // Full board state

ai

game.ai(options?) - Calculate the best move using AI. Returns both the move and board state.

Params:

  • options object (optional) - Configuration options:
    • level number (optional) - AI difficulty level (1-5). See Computer AI section. Default: 3
    • play boolean (optional) - Whether to apply the move to the game. Default: true. If false, returns the move without modifying the game state, and board will contain the current state (before the move).
    • analysis boolean (optional) - If true, also returns an analysis payload containing all root legal moves scored by the engine's search (sorted best → worst). Default: false.
    • ttSizeMB number (optional) - Transposition table size in MB (0 to disable, 0.25-256). Default: auto-scaled by AI level. See Auto-Scaling Transposition Table for details.
    • depth object (optional) - Override AI search depth parameters. Omitted fields fall back to the level's defaults (see Computer AI table).
      • base number (optional) - Base search depth. Integer > 0.
      • extended number (optional) - Max adaptive extension depth. Integer 0-3.
      • check boolean (optional) - Enable check extensions.
      • quiescence number (optional) - Quiescence search depth. Integer >= 0.

Returns: { move: HistoryEntry, board: BoardConfig, analysis?: Array<{ move: HistoryEntry, score: number }>, depth?: number, nodesSearched?: number, bestScore?: number } - Object containing the move and board state (current state if play=false, updated state if play=true). Extra fields are returned only when analysis: true.

import type { HistoryEntry, BoardConfig } from 'js-chess-engine'

// Play the move (default behavior)
const result1 = game.ai({ level: 4 })
console.log(result1.move)       // {"E2": "E4"}
console.log(result1.board.turn) // "black" (updated after move)

// Analysis mode: get move without applying it
const result2 = game.ai({ level: 4, play: false })
console.log(result2.move)       // {"E2": "E4"}
console.log(result2.board.turn) // "white" (current state, before move)

// Root move scoring (debug/inspection)
const result2b = game.ai({ level: 4, play: false, analysis: true })
console.log(result2b.analysis?.slice(0, 5)) // [{ move: {"E2": "E4"}, score: 12 }, ...]
console.log(result2b.bestScore)
console.log(result2b.depth)
console.log(result2b.nodesSearched)

// Use default level 3
const result3 = game.ai()
console.log(result3.move) // AI move with level 3

// TT size auto-scales by level (see Auto-Scaling Transposition Table section)
const result4 = game.ai({ level: 5 })
console.log(result4.move) // Level 5: 40 MB Node.js / 20 MB browser (auto)

// Override TT size manually if needed
const result5 = game.ai({ level: 3, ttSizeMB: 128 })
console.log(result5.move) // Force 128MB cache

// Ultra-lightweight mode for low-end devices
const result6 = game.ai({ level: 2, ttSizeMB: 0.5 })
console.log(result6.move) // Force 512KB cache

// Custom depth overrides (use level 3 defaults, but increase base depth)
const result7 = game.ai({ level: 3, depth: { base: 5 } })

// Full depth control: deep search, no extensions, no quiescence
const result8 = game.ai({ level: 1, depth: { base: 4, extended: 0, check: false, quiescence: 0 } })

getHistory

game.getHistory() - Get all played moves with board states.

Returns: Array of objects containing move and resulting board configuration

const history = game.getHistory()
// [
//   { move: {"E2": "E4"}, pieces: {...}, turn: "black", ... },
//   { move: {"E7": "E5"}, pieces: {...}, turn: "white", ... }
// ]

printToConsole

game.printToConsole() - Print an ASCII representation of the chessboard to console.

game.printToConsole()
//   +---+---+---+---+---+---+---+---+
// 8 | r | n | b | q | k | b | n | r |
//   +---+---+---+---+---+---+---+---+
// 7 | p | p | p | p | p | p | p | p |
//   +---+---+---+---+---+---+---+---+
// ...

exportJson

game.exportJson() - Export current game state as a JSON BoardConfig object.

Returns: BoardConfig - Full board configuration

import type { BoardConfig } from 'js-chess-engine'

const config: BoardConfig = game.exportJson()

exportFEN

game.exportFEN() - Export current game state as a FEN string.

Returns: string - FEN notation

const fen: string = game.exportFEN()
// "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"

Option 2 - Without in-memory

Call stateless functions directly without creating a Game object. This is ideal for serverless environments or when you want to manage state externally.

import { move, moves, status, aiMove, ai, getFen } from 'js-chess-engine'
import type { BoardConfig, MovesMap } from 'js-chess-engine'

These functions require a board configuration for each call (either a BoardConfig object or FEN string).

API Description

moves

moves(boardConfiguration) - Get all legal moves for the current player.

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration (JSON object or FEN string)

Returns: MovesMap - Object mapping from-squares to arrays of to-squares

import { moves } from 'js-chess-engine'
import type { MovesMap, BoardConfig } from 'js-chess-engine'

// From BoardConfig object
const config: BoardConfig = { /* ... */ }
const allMoves: MovesMap = moves(config)
// {"E2": ["E3", "E4"], "B1": ["A3", "C3"], ...}

// From FEN string
const fenMoves: MovesMap = moves('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1')

status

status(boardConfiguration) - Get calculated board configuration with current game status. Useful for converting FEN to JSON.

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration

Returns: BoardConfig - Full board configuration with status

import { status } from 'js-chess-engine'
import type { BoardConfig } from 'js-chess-engine'

// Convert FEN to JSON
const config: BoardConfig = status('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1')
console.log(config.turn)      // "black"
console.log(config.check)     // false
console.log(config.checkMate) // false

getFen

getFen(boardConfiguration) - Convert board configuration to FEN string.

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration

Returns: string - FEN notation

import { getFen } from 'js-chess-engine'
import type { BoardConfig } from 'js-chess-engine'

const config: BoardConfig = { /* ... */ }
const fen: string = getFen(config)

move

move(boardConfiguration, from, to) - Perform a move and get the new board state. Returns full BoardConfig object (breaking change from v1).

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration
  • from string (mandatory) - Starting square (case-insensitive)
  • to string (mandatory) - Destination square (case-insensitive)

Returns: BoardConfig - Full board configuration after the move

import { move } from 'js-chess-engine'
import type { BoardConfig } from 'js-chess-engine'

// Move from FEN string
const config1: BoardConfig = move('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', 'E2', 'E4')

// Move on existing config
const config2: BoardConfig = move(config1, 'E7', 'E5')

aiMove

⚠️ DEPRECATED: This function will be removed in v3.0.0. Use ai() instead, which returns both the move and board state.

aiMove(boardConfiguration, level) - Calculate and return the best move using AI. Returns only the move (v1 API compatible).

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration
  • level AILevel (optional) - AI difficulty level (1-5). Default: 3

Returns: HistoryEntry - The played move (e.g., {"E2": "E4"})

import { aiMove } from 'js-chess-engine'
import type { HistoryEntry, AILevel } from 'js-chess-engine'

const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
const level: AILevel = 4
const move: HistoryEntry = aiMove(fen, level)
console.log(move) // {"E2": "E4"}

// RECOMMENDED: Use ai() instead
import { ai } from 'js-chess-engine'
const result = ai(fen, { level: 4 })
console.log(result.move)  // {"E2": "E4"}
console.log(result.board) // Full board state

ai

ai(boardConfiguration, options?) - Calculate the best move using AI. Returns both the move and board state.

Params:

  • boardConfiguration BoardConfig | string (mandatory) - Board configuration
  • options object (optional) - Configuration options:
    • level number (optional) - AI difficulty level (1-5). Default: 3
    • play boolean (optional) - Whether to apply the move to the board. Default: true. If false, returns the move without modifying the board state, and board will contain the current state (before the move).
    • analysis boolean (optional) - If true, also returns an analysis payload containing all root legal moves scored by the engine's search (sorted best → worst). Default: false.
    • ttSizeMB number (optional) - Transposition table size in MB (0 to disable, 0.25-256). Default: auto-scaled by AI level. See Auto-Scaling Transposition Table for details.
    • depth object (optional) - Override AI search depth parameters. Omitted fields fall back to the level's defaults (see Computer AI table).
      • base number (optional) - Base search depth. Integer > 0.
      • extended number (optional) - Max adaptive extension depth. Integer 0-3.
      • check boolean (optional) - Enable check extensions.
      • quiescence number (optional) - Quiescence search depth. Integer >= 0.

Returns: { move: HistoryEntry, board: BoardConfig, analysis?: Array<{ move: HistoryEntry, score: number }>, depth?: number, nodesSearched?: number, bestScore?: number } - Object containing the move and board state (current state if play=false, updated state if play=true). Extra fields are returned only when analysis: true.

import { ai } from 'js-chess-engine'
import type { HistoryEntry, BoardConfig } from 'js-chess-engine'

const fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'

// Play the move (default behavior)
const result1 = ai(fen, { level: 4 })
console.log(result1.move)       // {"E2": "E4"}
console.log(result1.board.turn) // "black" (updated after move)

// Analysis mode: get move without applying it
const result2 = ai(fen, { level: 4, play: false })
console.log(result2.move)       // {"E2": "E4"}
console.log(result2.board.turn) // "white" (current state, before move)

// Root move scoring (debug/inspection)
const result2b = ai(fen, { level: 4, play: false, analysis: true })
console.log(result2b.analysis?.slice(0, 5)) // [{ move: {"E2": "E4"}, score: 12 }, ...]
console.log(result2b.bestScore)
console.log(result2b.depth)
console.log(result2b.nodesSearched)

// Use default level 3
const result3 = ai(fen)
console.log(result3.move) // AI move with level 3

// TT size auto-scales by level (see Auto-Scaling Transposition Table section)
const result4 = ai(fen, { level: 5 })
console.log(result4.move) // Level 5: 40 MB Node.js / 20 MB browser (auto)

// Override TT size manually if needed
const result5 = ai(fen, { level: 3, ttSizeMB: 128 })
console.log(result5.move) // Force 128MB cache

// Ultra-lightweight mode for low-end devices
const result6 = ai(fen, { level: 2, ttSizeMB: 0.5 })
console.log(result6.move) // Force 512KB cache

// Custom depth overrides
const result7 = ai(fen, { level: 3, depth: { base: 5, quiescence: 3 } })

Board Configuration

Board configuration can be represented as either a JSON object (BoardConfig) or a FEN string.

JSON Format

The JSON format is convenient for modern applications where state is represented as objects (React, Redux, etc.).

import type { BoardConfig } from 'js-chess-engine'

const config: BoardConfig = {
  "turn": "black",
  "pieces": {
    "E1": "K",
    "C1": "B",
    "E8": "k"
  },
  "isFinished": false,
  "check": false,
  "checkMate": false,
  "staleMate": false,
  "castling": {
    "whiteLong": true,
    "whiteShort": true,
    "blackLong": true,
    "blackShort": true
  },
  "enPassant": "E6",
  "halfMove": 0,
  "fullMove": 1
}

turn - Player to move next. Values: "white" (default) or "black"

isFinished - true when the game is over (checkmate or stalemate). Default: false

check - true when the current player is in check. Default: false

checkMate - true when the current player is checkmated. Default: false

staleMate - true when the current player is stalemated (no legal moves but not in check). Default: false

castling - Castling availability for each side. true means castling is still possible. Default: all true

  • whiteLong (queenside) - White king moves from E1 to C1
  • whiteShort (kingside) - White king moves from E1 to G1
  • blackLong (queenside) - Black king moves from E8 to C8
  • blackShort (kingside) - Black king moves from E8 to G8

enPassant - If a pawn just made a two-square move, this is the square "behind" the pawn for en passant capture. Default: null

halfMove - Number of halfmoves since the last capture or pawn advance. Used for the fifty-move rule. Default: 0

fullMove - Full move number. Starts at 1 and increments after Black's move. Default: 1

pieces - Pieces on the board using FEN notation:

Piece White Black
Pawn P p
Knight N n
Bishop B b
Rook R r
Queen Q q
King K k

FEN Format

You can also use Forsyth–Edwards Notation (FEN):

import { move } from 'js-chess-engine'

const fen = 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
const newConfig = move(fen, 'H7', 'H5')
console.log(newConfig)
// BoardConfig object with updated position

Computer AI

The engine includes a sophisticated AI based on the Minimax algorithm with alpha-beta pruning, enhanced with advanced performance optimizations. There are five difficulty levels:

Level Alias Description Base Depth Extended Depth Check Ext. Quiescence Total Max
1 Beginner Very weak play, minimal lookahead 1 ply +0 ply +0 ply +0 ply 1 ply
2 Easy Suitable for new chess players 2 ply +0 ply +1 ply +0 ply 3 ply
3 Intermediate Balanced difficulty (default) 2 ply +1 ply +1 ply +1 ply 5 ply
4 Advanced Strong play with deeper search 3 ply +2 ply +1 ply +2 ply 8 ply
5 Expert Very strong play, deep search 4 ply +3 ply +1 ply +4 ply 12 ply

Depth Components:

  • Base Depth: Minimum search depth for the AI level
  • Extended Depth: Additional adaptive depth added in simplified positions (endgames, constrained moves)
  • Check Extension: Extra ply when a move gives check (prevents tactical oversights)
  • Quiescence Search: Additional plies to evaluate forcing moves (captures/promotions) at leaf nodes, preventing horizon effect blunders
  • Total Max: Maximum possible search depth in tactical positions (all extensions combined)

Performance: Response time increases with level (deeper search + larger transposition table). Exact timings vary a lot by CPU, position complexity, and cache size, so the repo includes a benchmark script—run npm run benchmark to measure performance on your machine.

import { Game } from 'js-chess-engine'
import type { AILevel } from 'js-chess-engine'

const game = new Game()

// Different difficulty levels
game.aiMove(1)  // Beginner
game.aiMove(2)  // Easy
game.aiMove(3)  // Intermediate (default)
game.aiMove(4)  // Advanced
game.aiMove(5)  // Expert

Implementation Highlights:

  • Alpha-beta pruning with transposition table (auto-scales by AI level for optimal memory usage)
  • Principal Variation Search (PVS) for faster node pruning
  • Advanced move ordering (PV moves, MVV-LVA captures, killer moves)
  • Iterative deepening for optimal move ordering
  • Position evaluation with material balance and piece-square tables
  • Smart environment detection and level-based memory scaling for optimal performance across platforms

Auto-Scaling Transposition Table (Smart Memory Management) {#transposition-table}

The engine automatically adjusts cache size based on AI level and environment:

AI Level Node.js Cache Browser Cache Use Case
1 0.5 MB 0.25 MB Lightweight, fast responses
2 1 MB 0.5 MB Mobile-friendly performance
3 4 MB 2 MB Balanced (default)
4 16 MB 8 MB Strong tactical play
5 40 MB 20 MB Maximum strength

Lower levels use less memory for faster responses, higher levels use more for better move quality. Browser cache sizes are appropriate for modern devices (2024+). Override with ttSizeMB option if needed.

Note: TT size directly affects AI strength. Larger cache = better move ordering and fewer redundant searches, resulting in stronger play.

📚 Complete AI Implementation Guide →

For comprehensive technical documentation including algorithms, data structures, optimization techniques, and developer guides, see the AI implementation documentation.

Hints

  • Pawn Promotion: When a pawn reaches the opposite end of the board, it is automatically promoted to a Queen. If you want the player to choose the promotion piece in your application, use the setPiece() method to replace the queen with the desired piece.

  • Castling: Castling moves are included in the moves returned by moves(). When a king moves two squares (castling), the rook automatically moves as well.

  • Fifty-Move Rule: The halfMove counter is calculated automatically, but the fifty-move rule is not enforced by the engine. You can implement this rule in your application if needed.

TypeScript Support

Version 2.0 is written entirely in TypeScript and exports all necessary types:

import { Game } from 'js-chess-engine'
import type {
  // Board types
  BoardConfig,
  PieceSymbol,
  Square,
  Color,
  MovesMap,
  CastlingRights,
  HistoryEntry,

  // AI types
  AILevel,

  // Piece types
  PieceType
} from 'js-chess-engine'

See /src/types/board.types.ts and /src/types/ai.types.ts for complete type definitions.

Collaboration

Collaborators are welcome. Please ensure your code passes TypeScript type checking and all tests before submitting a pull request:

npm run typecheck  # TypeScript type checking
npm run test       # Run test suite

If possible, use commit message prefixes like feat: or fix: - the changelog is generated from these.

TODO

  • FEN validation
  • Additional endgame tablebase support

CHANGELOG

Changelog can be found HERE.

In conclusion - why another chess engine?

I am not a chess pro. My father is.

When I was ten, I had an Atari (with Turbo Basic), and I was hoping for a new PC. My father told me: "Make me a computer program which beats me in chess, and I'll buy you a new PC."

Me (and my brother) as kids debugging our way into a chess engine on pentium 1

Obviously, it was a trap and I failed. Twenty years later, it came back to my mind, and I decided to finish what I started. This is version 2.0 - a complete TypeScript rewrite with improved performance and architecture.

Breaking Changes from v1

Version 2.0 is a complete TypeScript rewrite with significant API changes. While method names remain the same, several return types have changed:

1. moves(square) Return Type Changed

v1 Behavior:

const game = new Game()
game.moves('E2')  // Returns array: ["E3", "E4"]

v2 Behavior:

const game = new Game()
game.moves('E2')  // Returns object: {"E2": ["E3", "E4"]}

2. move() Return Type Changed

v1 Behavior:

game.move('E2', 'E4')  // Returns move object: {"E2": "E4"}

v2 Behavior:

game.move('E2', 'E4')  // Returns full BoardConfig object

3. aiMove() API - Now v1 Compatible ✅

The aiMove() function has been restored to v1 API compatibility.

v1 Behavior:

aiMove(config, 2)  // Returns move object: {"E2": "E4"}

v2 Behavior (Current):

aiMove(config, 3)  // Returns move object: {"E2": "E4"} ✅ v1 compatible

New ai() function - For users who need both move and board state:

ai(config, 3)  // Returns: { move: {"E2": "E4"}, board: {...} }

⚠️ DEPRECATION NOTICE: aiMove() is deprecated and will be removed in v3.0.0. Migrate to ai() for better functionality.

Migration:

// Old (deprecated)
const move = game.aiMove(3)
const board = game.exportJson()

// New (recommended)
const result = game.ai({ level: 3 })
console.log(result.move)  // Same move object
console.log(result.board) // Board state included

4. AI Difficulty Levels Changed

  • v1: Levels 0-4 (0=easiest, 4=hardest)
  • v2: Levels 1-5 (1=easiest, 5=hardest)

Migration:

  • Level 0 → Level 1 (Beginner)
  • Level 1 → Level 2 (Easy)
  • Level 2 → Level 3 (Intermediate, default)
  • Level 3 → Level 4 (Advanced)
  • Level 4 → Level 5 (Expert)

The default level has changed from 2 to 3 to maintain similar difficulty in the middle range.

5. New staleMate Field

v2 adds explicit staleMate: boolean field to BoardConfig. In v1, stalemate was inferred from isFinished && !checkMate. isFinished remains for backward compatibility but can be removed in future versions.

6. Node.js Version Requirement

  • v1: Node.js >=20 <21
  • v2: Node.js >=24

What Stayed the Same

  • ✅ Game class constructor signature
  • ✅ Method names (move, moves, aiMove, etc.)
  • ✅ Board formats (JSON and FEN)
  • ✅ Chess rules (castling, en passant, promotions)
  • ✅ Square notation (case-insensitive: 'E2' or 'e2')

About

Complete TypeScript chess engine with zero dependencies for Node.js >=24 and browsers. Features configurable AI (5 predefined difficulty levels), stateful/stateless APIs, and supports JSON and FEN formats.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 6