{"id":30292019,"url":"https://github.com/timbeyer/dancing-links","last_synced_at":"2026-01-18T05:48:05.258Z","repository":{"id":32623311,"uuid":"36209066","full_name":"TimBeyer/dancing-links","owner":"TimBeyer","description":"Dancing Links","archived":false,"fork":false,"pushed_at":"2026-01-15T03:21:19.000Z","size":877,"stargazers_count":9,"open_issues_count":6,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-15T04:41:41.677Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimBeyer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-05-25T04:07:57.000Z","updated_at":"2026-01-15T03:21:22.000Z","dependencies_parsed_at":"2025-07-30T09:16:39.428Z","dependency_job_id":"e343cc34-b79d-4a10-be67-d7468d2712c8","html_url":"https://github.com/TimBeyer/dancing-links","commit_stats":null,"previous_names":["timbeyer/dancing-links"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/TimBeyer/dancing-links","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimBeyer%2Fdancing-links","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimBeyer%2Fdancing-links/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimBeyer%2Fdancing-links/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimBeyer%2Fdancing-links/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimBeyer","download_url":"https://codeload.github.com/TimBeyer/dancing-links/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimBeyer%2Fdancing-links/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28531554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-17T00:05:18.575Z","updated_at":"2026-01-18T05:48:05.249Z","avatar_url":"https://github.com/TimBeyer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dancing-links\n\n## About\n\nThis is an implementation of Knuth's DLX to solve the exact cover problem.\nIt is a port of [Knuth's literate dancing links implementation](https://cs.stanford.edu/~knuth/programs/dance.w) and supports primary and secondary constraints, and returning custom data in addition to row indices.\n\nThere are no external dependencies and there is full TypeScript support.\n\nIt is currently [the fastest](#benchmarks) Dancing Links implementation in JavaScript.\n\n## Usage\n\n### Basic Example\n\n```ts\nimport { DancingLinks } from 'dancing-links'\n\nconst dlx = new DancingLinks\u003cstring\u003e()\nconst solver = dlx.createSolver({ columns: 3 })\n\nsolver.addSparseConstraint('row1', [0, 2]) // Constraint active in columns 0 and 2\nsolver.addSparseConstraint('row2', [1]) // Constraint active in column 1\nsolver.addSparseConstraint('row3', [0, 1]) // Constraint active in columns 0 and 1\n\nconst solutions = solver.findAll()\n// Returns: [[{ data: 'row1', index: 0 }, { data: 'row2', index: 1 }]]\n```\n\n### Constraint Formats\n\n#### Sparse Constraints (Recommended)\n\n**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.\n\n```ts\nconst solver = dlx.createSolver({ columns: 100 })\n\n// ⚡ EFFICIENT: Only specify active columns\nsolver.addSparseConstraint('constraint1', [0, 15, 42, 87])\nsolver.addSparseConstraint('constraint2', [1, 16, 43, 99])\n\n// Batch operations for better performance\nconst constraints = [\n  { data: 'batch1', columnIndices: [0, 10, 20] },\n  { data: 'batch2', columnIndices: [5, 15, 25] },\n  { data: 'batch3', columnIndices: [2, 12, 22] }\n]\nsolver.addSparseConstraints(constraints)\n```\n\n#### Binary Constraints\n\nUse binary constraints when it's more convenient for your encoding logic or when you already have constraint data in binary format:\n\n```ts\nconst solver = dlx.createSolver({ columns: 4 })\n\n// Convenient when encoding naturally produces binary arrays\nsolver.addBinaryConstraint('row1', [1, 0, 1, 0])\nsolver.addBinaryConstraint('row2', [0, 1, 0, 1])\nsolver.addBinaryConstraint('row3', [1, 1, 0, 0])\n\n// Batch operations\nconst binaryConstraints = [\n  { data: 'batch1', columnValues: [1, 0, 1, 0] },\n  { data: 'batch2', columnValues: [0, 1, 0, 1] }\n]\nsolver.addBinaryConstraints(binaryConstraints)\n```\n\n### Constraint Templates\n\nFor 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:\n\n```ts\n// Create template with base constraints\nconst template = dlx.createSolverTemplate({ columns: 20 })\ntemplate.addSparseConstraint('base1', [0, 5, 10])\ntemplate.addSparseConstraint('base2', [1, 6, 11])\n\n// Create multiple solvers from the same template\nconst solver1 = template.createSolver()\nsolver1.addSparseConstraint('extra1', [2, 7, 12])\nconst solutions1 = solver1.findAll()\n\nconst solver2 = template.createSolver()\nsolver2.addSparseConstraint('extra2', [3, 8, 13])\nconst solutions2 = solver2.findAll()\n```\n\n### Complex Constraints (Primary + Secondary)\n\nFor problems requiring both primary constraints (must be covered exactly once) and secondary constraints (optional - may be left uncovered, but if covered, allow no collisions):\n\n```ts\nconst solver = dlx.createSolver({\n  primaryColumns: 2, // First 2 columns are primary\n  secondaryColumns: 2 // Next 2 columns are secondary\n})\n\n// Method 1: Add constraints separately\nsolver.addSparseConstraint('constraint1', {\n  primary: [0], // Must cover primary column 0\n  secondary: [1] // May cover secondary column 1 (optional, but no conflicts if used)\n})\n\n// Method 2: Add as binary constraint\nsolver.addBinaryConstraint('constraint2', {\n  primaryRow: [0, 1], // Binary values for primary columns\n  secondaryRow: [1, 0] // Binary values for secondary columns\n})\n```\n\n**Key difference between primary and secondary constraints:**\n\n- **Primary**: All primary columns MUST be covered exactly once in any valid solution\n- **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)\n\n### Solution Methods\n\n```ts\n// Find one solution\nconst oneSolution = solver.findOne()\n\n// Find all solutions\nconst allSolutions = solver.findAll()\n\n// Find up to N solutions\nconst limitedSolutions = solver.find(10)\n```\n\n### Generator Interface (Streaming Solutions)\n\nFor large solution spaces or when you need early termination, use the generator interface:\n\n```ts\n// Stream solutions one at a time\nconst generator = solver.createGenerator()\n\nlet solutionCount = 0\nfor (const solution of generator) {\n  console.log('Found solution:', solution)\n\n  solutionCount++\n  if (solutionCount \u003e= 5) {\n    // Stop after finding 5 solutions\n    break\n  }\n}\n\n// Manual iteration for full control\nconst generator2 = solver.createGenerator()\nlet result = generator2.next()\nwhile (!result.done) {\n  processSolution(result.value)\n  result = generator2.next()\n}\n```\n\nThe generator maintains search state between solutions, enabling memory-efficient streaming and early termination without computing all solutions upfront.\n\n## Examples\n\nThe [benchmark directory](https://github.com/TimBeyer/dancing-links/tree/master/benchmark) contains complete implementations for:\n\n- **N-Queens Problem**: Classical constraint satisfaction problem\n- **Pentomino Tiling**: 2D shape placement with rotation constraints\n- **Sudoku Solver**: Number placement with row/column/box constraints\n\nThese examples demonstrate encoding techniques for different problem types and show performance optimization strategies.\n\n## Benchmarks\n\nThis section contains performance comparisons against other JavaScript Dancing Links libraries, updated automatically during releases.\n\nAll benchmarks run on the same machine with identical test cases. Results show operations per second (higher is better).\n\n### All solutions to the sudoku\n\n| Library                 | Ops/Sec  | Relative Performance | Margin of Error |\n| ----------------------- | -------- | -------------------- | --------------- |\n| dancing-links (sparse)  | 12776.77 | **1.00x (fastest)**  | ±0.16%          |\n| dancing-links (binary)  | 5084.72  | 0.40x                | ±0.23%          |\n| dance                   | 2022.24  | 0.16x                | ±0.41%          |\n| dancing-links-algorithm | 1397.72  | 0.11x                | ±0.38%          |\n| dlxlib                  | 1274.87  | 0.10x                | ±0.71%          |\n\n### Finding one pentomino tiling on a 6x10 field\n\n| Library                | Ops/Sec | Relative Performance | Margin of Error |\n| ---------------------- | ------- | -------------------- | --------------- |\n| dancing-links (sparse) | 442.34  | **1.00x (fastest)**  | ±0.56%          |\n| dancing-links (binary) | 430.33  | 0.97x                | ±0.46%          |\n| dlxlib                 | 348.68  | 0.79x                | ±0.68%          |\n| dance                  | 84.99   | 0.19x                | ±0.79%          |\n\n### Finding ten pentomino tilings on a 6x10 field\n\n| Library                | Ops/Sec | Relative Performance | Margin of Error |\n| ---------------------- | ------- | -------------------- | --------------- |\n| dancing-links (sparse) | 68.74   | **1.00x (fastest)**  | ±1.12%          |\n| dlxlib                 | 68.32   | 0.99x                | ±1.11%          |\n| dancing-links (binary) | 67.45   | 0.98x                | ±1.14%          |\n| dance                  | 17.36   | 0.25x                | ±0.85%          |\n\n### Finding one hundred pentomino tilings on a 6x10 field\n\n| Library                | Ops/Sec | Relative Performance | Margin of Error |\n| ---------------------- | ------- | -------------------- | --------------- |\n| dancing-links (sparse) | 9.11    | **1.00x (fastest)**  | ±2.07%          |\n| dancing-links (binary) | 8.99    | 0.99x                | ±1.67%          |\n| dlxlib                 | 8.61    | 0.95x                | ±2.00%          |\n| dance                  | 2.37    | 0.26x                | ±1.85%          |\n\n**Testing Environment:**\n\n- Node.js v25.3.0\n- Test cases: Sudoku solving, pentomino tiling (1, 10, 100 solutions)\n\n_Last updated: 2026-01-15_\n\n## Contributing\n\nFor development information, performance benchmarking, profiling, and contribution guidelines, see [DEVELOPMENT.md](DEVELOPMENT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimbeyer%2Fdancing-links","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimbeyer%2Fdancing-links","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimbeyer%2Fdancing-links/lists"}