Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

dancing-links

TimBeyer1.3kMIT4.3.2TypeScript support: included

Fastest JS solver for exact cover problems using Dancing Links

dlx, dancing links, algorithm x, exact cover, knuth

readme

dancing-links

About

This is an implementation of Knuth's DLX to solve the exact cover problem. It is a port of Knuth's literate dancing links implementation and supports primary and secondary constraints, and returning custom data in addition to row indices.

There are no external dependencies and there is full TypeScript support.

It is currently the fastest Dancing Links implementation in JavaScript.

Usage

Basic Example

import { DancingLinks } from 'dancing-links'

const dlx = new DancingLinks<string>()
const solver = dlx.createSolver({ columns: 3 })

solver.addSparseConstraint('row1', [0, 2]) // Constraint active in columns 0 and 2
solver.addSparseConstraint('row2', [1]) // Constraint active in column 1
solver.addSparseConstraint('row3', [0, 1]) // Constraint active in columns 0 and 1

const solutions = solver.findAll()
// Returns: [[{ data: 'row1', index: 0 }, { data: 'row2', index: 1 }]]

Constraint Formats

Sparse constraints are the most efficient format - specify only the active column indices instead of full binary arrays. This reduces parsing overhead and memory usage, especially for problems with many columns.

const solver = dlx.createSolver({ columns: 100 })

// ⚡ EFFICIENT: Only specify active columns
solver.addSparseConstraint('constraint1', [0, 15, 42, 87])
solver.addSparseConstraint('constraint2', [1, 16, 43, 99])

// Batch operations for better performance
const constraints = [
  { data: 'batch1', columnIndices: [0, 10, 20] },
  { data: 'batch2', columnIndices: [5, 15, 25] },
  { data: 'batch3', columnIndices: [2, 12, 22] }
]
solver.addSparseConstraints(constraints)

Binary Constraints

Use binary constraints when it's more convenient for your encoding logic or when you already have constraint data in binary format:

const solver = dlx.createSolver({ columns: 4 })

// Convenient when encoding naturally produces binary arrays
solver.addBinaryConstraint('row1', [1, 0, 1, 0])
solver.addBinaryConstraint('row2', [0, 1, 0, 1])
solver.addBinaryConstraint('row3', [1, 1, 0, 0])

// Batch operations
const binaryConstraints = [
  { data: 'batch1', columnValues: [1, 0, 1, 0] },
  { data: 'batch2', columnValues: [0, 1, 0, 1] }
]
solver.addBinaryConstraints(binaryConstraints)

Constraint Templates

For problems with reusable constraint patterns, templates provide significant performance benefits by pre-processing base constraints. Templates are especially beneficial when using binary constraints, as the binary-to-sparse conversion happens once during template creation rather than every time you create a solver:

// Create template with base constraints
const template = dlx.createSolverTemplate({ columns: 20 })
template.addSparseConstraint('base1', [0, 5, 10])
template.addSparseConstraint('base2', [1, 6, 11])

// Create multiple solvers from the same template
const solver1 = template.createSolver()
solver1.addSparseConstraint('extra1', [2, 7, 12])
const solutions1 = solver1.findAll()

const solver2 = template.createSolver()
solver2.addSparseConstraint('extra2', [3, 8, 13])
const solutions2 = solver2.findAll()

Complex Constraints (Primary + Secondary)

For problems requiring both primary constraints (must be covered exactly once) and secondary constraints (optional - may be left uncovered, but if covered, allow no collisions):

const solver = dlx.createSolver({
  primaryColumns: 2, // First 2 columns are primary
  secondaryColumns: 2 // Next 2 columns are secondary
})

// Method 1: Add constraints separately
solver.addSparseConstraint('constraint1', {
  primary: [0], // Must cover primary column 0
  secondary: [1] // May cover secondary column 1 (optional, but no conflicts if used)
})

// Method 2: Add as binary constraint
solver.addBinaryConstraint('constraint2', {
  primaryRow: [0, 1], // Binary values for primary columns
  secondaryRow: [1, 0] // Binary values for secondary columns
})

Key difference between primary and secondary constraints:

  • Primary: All primary columns MUST be covered exactly once in any valid solution
  • Secondary: Secondary columns are optional - they can be left uncovered, but if a secondary column IS covered, only one constraint can cover it (no collisions allowed)

Solution Methods

// Find one solution
const oneSolution = solver.findOne()

// Find all solutions
const allSolutions = solver.findAll()

// Find up to N solutions
const limitedSolutions = solver.find(10)

Generator Interface (Streaming Solutions)

For large solution spaces or when you need early termination, use the generator interface:

// Stream solutions one at a time
const generator = solver.createGenerator()

let solutionCount = 0
for (const solution of generator) {
  console.log('Found solution:', solution)

  solutionCount++
  if (solutionCount >= 5) {
    // Stop after finding 5 solutions
    break
  }
}

// Manual iteration for full control
const generator2 = solver.createGenerator()
let result = generator2.next()
while (!result.done) {
  processSolution(result.value)
  result = generator2.next()
}

The generator maintains search state between solutions, enabling memory-efficient streaming and early termination without computing all solutions upfront.

Examples

The benchmark directory contains complete implementations for:

  • N-Queens Problem: Classical constraint satisfaction problem
  • Pentomino Tiling: 2D shape placement with rotation constraints
  • Sudoku Solver: Number placement with row/column/box constraints

These examples demonstrate encoding techniques for different problem types and show performance optimization strategies.

Benchmarks

This section contains performance comparisons against other JavaScript Dancing Links libraries, updated automatically during releases.

All benchmarks run on the same machine with identical test cases. Results show operations per second (higher is better).

All solutions to the sudoku

Library Ops/Sec Relative Performance Margin of Error
dancing-links (sparse) 7118.16 1.00x (fastest) ±0.28%
dancing-links (binary) 2822.15 0.40x ±0.87%
dance 1255.93 0.18x ±0.88%
dlxlib 818.09 0.11x ±2.95%
dancing-links-algorithm 757.39 0.11x ±0.82%

Finding one pentomino tiling on a 6x10 field

Library Ops/Sec Relative Performance Margin of Error
dancing-links (sparse) 182.26 1.00x (fastest) ±0.78%
dancing-links (binary) 177.61 0.97x ±0.21%
dlxlib 161.60 0.89x ±0.20%
dance 48.93 0.27x ±0.64%

Finding ten pentomino tilings on a 6x10 field

Library Ops/Sec Relative Performance Margin of Error
dancing-links (sparse) 27.66 1.00x (fastest) ±0.35%
dancing-links (binary) 27.59 1.00x ±0.18%
dlxlib 27.16 0.98x ±0.39%
dance 10.48 0.38x ±0.85%

Finding one hundred pentomino tilings on a 6x10 field

Library Ops/Sec Relative Performance Margin of Error
dancing-links (binary) 3.90 1.00x (fastest) ±0.53%
dancing-links (sparse) 3.89 1.00x ±0.66%
dlxlib 3.74 0.96x ±0.37%
dance 1.50 0.38x ±0.78%

Testing Environment:

  • Node.js v24.6.0
  • Test cases: Sudoku solving, pentomino tiling (1, 10, 100 solutions)

Last updated: 2025-08-15

Contributing

For development information, performance benchmarking, profiling, and contribution guidelines, see DEVELOPMENT.md.

changelog

Changelog

4.3.2 (2025-08-15)

Performance Improvements

  • use switch/case instead of dictionary lookup for state machine (d695c26)

4.3.1 (2025-08-08)

4.3.0 (2025-08-07)

Features

  • add fast solver with batch constraint optimization (317064d)

Performance Improvements

  • unify constraint system with efficient ConstraintRow POJOs (1fefdcb)

4.2.2 (2025-08-07)

4.2.0 (2025-08-06)

Bug Fixes

  • correct path resolution and JSON parsing in benchmark docs script (d168af8)
  • eliminate mutable instance state from benchmark solvers (784d71c)
  • format ops/sec to 2 decimal places in benchmark results (442006f)
  • preserve full precision in benchmark results (ba5deed)
  • properly separate main and dev TypeScript configurations (488bb50)
  • replace any type with proper types in template solver (f83adba)
  • round margin percentages to 2 decimal places in benchmark comparisons (2071d38)
  • working modular benchmark system (370e45a)

Features

  • add automated benchmark documentation system (4534e2a)
  • implement descriptive solver names with static name property (ae67510)
  • include binary solver in competitive benchmarks for fair comparison (1f7d445)
  • make benchmark results directly visible in README (69b6005)
  • update README benchmarks and add auto-formatting to doc script (593b713)

4.1.0 (2025-08-06)

Bug Fixes

  • add generator benchmark to single pentomino tiling test (9f01206)

Features

  • add resumable generator interface for streaming solutions (0b229b3)

4.0.0 (2025-08-05)

Bug Fixes

  • address codebase review issues and improve code quality (501ce0e)
  • disable commitlint footer line length rule and format code (15b9888)
  • replace any with proper union types in factory methods (6f30676)
  • resolve benchmark formatting and CI comparison issues (246fc7a)
  • resolve linting errors and improve test coverage (61d4b4d)
  • resolve TypeScript compilation error in constraint handlers (2b37f50)
  • support positional filename argument in benchmark JSON mode (4b9ca83)

Code Refactoring

  • remove deprecated legacy API and clean up codebase (5810ea3)

Features

  • add backward compatibility with deprecation notices (0e0d712)
  • complete high-performance caching API implementation (43174b6)
  • consolidate benchmark system with unified CLI interface (93592fe)
  • deprecate remaining legacy API functions (f2dbc89)
  • implement dual interface with sparse and binary constraint support (176e1ef)
  • implement high-performance constraint caching API (91a109d)
  • implement strongly typed factory methods with conditional types (aae13ce)
  • implement type-safe SolverTemplate with upfront configuration validation (d822b0b)

Performance Improvements

  • add comprehensive benchmarks for new caching API (00aa7e9)
  • convert Row interface to class for V8 optimization (7036713)
  • eliminate unnecessary array copying in sparse constraints (e498c76)
  • implement batch operations with runtime caching optimizations (7dc2c89)
  • implement optional validation for production performance (fd942ab)
  • optimize benchmarks to show real-world API usage patterns (b05d019)
  • optimize constraint handlers and fix misleading parameter naming (4854099)
  • optimize constraint processing with single-pass algorithms (d1d8ea1)
  • replace abstract inheritance with interface delegation pattern (58c5ad9)

BREAKING CHANGES

  • Legacy functional API (findOne, findAll, find, findRaw) has been removed. Use the new DancingLinks class API instead.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

3.4.0 (2025-08-02)

Bug Fixes

  • address codebase review issues and improve code quality (501ce0e)
  • replace any with proper union types in factory methods (6f30676)
  • resolve benchmark formatting and CI comparison issues (246fc7a)
  • resolve linting errors and improve test coverage (61d4b4d)
  • resolve TypeScript compilation error in constraint handlers (2b37f50)
  • support positional filename argument in benchmark JSON mode (4b9ca83)

Features

  • add backward compatibility with deprecation notices (0e0d712)
  • complete high-performance caching API implementation (43174b6)
  • consolidate benchmark system with unified CLI interface (93592fe)
  • deprecate remaining legacy API functions (f2dbc89)
  • implement dual interface with sparse and binary constraint support (176e1ef)
  • implement high-performance constraint caching API (91a109d)
  • implement strongly typed factory methods with conditional types (aae13ce)
  • implement type-safe SolverTemplate with upfront configuration validation (d822b0b)

Performance Improvements

  • add comprehensive benchmarks for new caching API (00aa7e9)
  • convert Row interface to class for V8 optimization (7036713)
  • eliminate unnecessary array copying in sparse constraints (e498c76)
  • implement batch operations with runtime caching optimizations (7dc2c89)
  • implement optional validation for production performance (fd942ab)
  • optimize benchmarks to show real-world API usage patterns (b05d019)
  • optimize constraint handlers and fix misleading parameter naming (4854099)
  • optimize constraint processing with single-pass algorithms (d1d8ea1)
  • replace abstract inheritance with interface delegation pattern (58c5ad9)

3.3.0 (2025-08-01)

Bug Fixes

  • add compare-benchmarks npm script and use in workflow (0d3f986)
  • add null checks for strict TypeScript compliance (a89b6e2)
  • address additional Copilot review comments (cfccc1d)
  • address Copilot review comments (c7f3cc9)
  • calculate merge-base in comment job for proper display (2ec7983)
  • correct direct execution detection in comparison script (c6066ee)
  • improve benchmark error handling (fd2a2b0)
  • include scripts folder in dev TypeScript build (8a2ebcb)
  • output pure JSON by writing to file instead of stdout (bb819ef)
  • parse benchmark sections correctly (5341f51)
  • remove old benchmark file (983049d)
  • use direct package.json check for script existence (56c6769)

Features

  • add PR benchmark comparison with automated comments (d1efae8)
  • handle first PR scenario gracefully when baseline unavailable (2812fc8)
  • improve benchmark error reporting and handle missing scripts (1e8533d)
  • structured JSON benchmarks (ab6c3ce)

Performance Improvements

  • remove duplicate build step in benchmark workflow (a8d8836)

3.2.2 (2025-07-31)

3.2.1 (2025-07-31)

Bug Fixes

  • use correct npm script for coverage in test workflow (335db17)

3.2.0 (2025-07-31)

Bug Fixes

  • correct typo 'sodoku' to 'sudoku' in benchmark output (6a550e1)
  • enable profiler with vscode compatible output format (84352c7)

Features

  • add development benchmark comparing Original AoS vs SoA (0e4d3fb)
  • implement Struct-of-Arrays data structures for Dancing Links (89613e6)
  • separate fast benchmarks from library comparison benchmarks (37a289b)
  • use classes instead of POJOs for better lookup performance (584ba0e)

Performance Improvements

  • complete systematic optimization testing (085ada8)
  • phase 1a enhanced column selection heuristic - reverted (b4e3340)
  • phase 1b column length tracking - implementation failed (f3d9a98)
  • phase 2a unit propagation - kept (d2d3a84)
  • phase 2b memory layout optimization - reverted (a668d6a)
  • phase 2b memory layout optimization retry - reverted (bdb4fcf)
  • phase 3a symmetry breaking - conceptual failure (21f8a47)
  • remaining opt 1 cache warming - reverted (f546461)
  • remaining opt 4 extended unit propagation - reverted (0acf8bc)
  • systematic optimization testing - Tests 1-5 complete (3474482)
  • test 7 inline forward function - reverted (b5fa57b)
  • test 8 local variable caching - reverted (6248b88)
  • test 9 pre-calculate next pointers - kept (62a78c8)

3.1.0 (2025-07-15)

Bug Fixes

  • add workflow dependency and build step to release workflow (70aa08f)
  • replace custom commitlint logic with wagoid/commitlint-github-action (837f7ec)
  • revert CHANGELOG.md formatting and remove commitlint script (01f4e9a)

Features

  • implement release automation formatting and conventional commits improvements (192a55a)

3.0.0 (2025-07-15)

  • feat!: require Node.js 20+ and add conventional commits guidelines (8fab697)

Bug Fixes

BREAKING CHANGES

  • Node.js 20+ is now required. The minimum supported Node.js version has been increased from 18 to 20 to align with the modernized CI/CD pipeline and take advantage of newer Node.js features.

Co-authored-by: TimBeyer 2362075+TimBeyer@users.noreply.github.com