Dyalog
Precomputing the grid's contention map and passing it as an inner operand eliminates redundant calculations, allowing the breadth-first solver to resolve standard 9x9 puzzles instantly.
AI-assisted summary of A Sudoku Solver in APL — original source. Check the original for full context.
Source published:
Key ideas
- A Sudoku solver is constructed in APL based on an algorithm by Arthur Whitney, demonstrated incrementally starting with a 4×4 board before scaling to full 9×9 puzzles. (0:00)
- Cell conflicts are modeled by joining row, column, and sub-box indices into coordinate triples, using outer product comparisons to generate a rank-4 boolean contention map (`cmap`). (1:17)
- The solver employs a breadth-first search by identifying all empty cells (zeros) and folding a placement expansion function (`px`) over them across candidate board states. (6:33)
- Precomputing the static contention map once at the top level and threading it down as an operand avoids thousands of redundant recalculations, enabling efficient solving of standard 9×9 puzzles. (8:01)
Detailed notes
## Background Published on YouTube by Dyalog on October 27, 2012, this video demonstrates the development of a Sudoku solver written in APL. Based on an algorithm by Arthur Whitney and presented by John Scholes, the description points viewers to experiment with the code at TryAPL.org and consult notes on the breadth-first search implementation.
## TL;DR The video walks through constructing an elegant Sudoku solver from first principles in APL, initially modeling the logic on a 4×4 grid before scaling to a standard 9×9 board. It establishes cell coordinates across rows, columns, and sub-boxes, then generates a boolean contention map to identify valid candidate numbers for any empty cell. By designing placement operators and folding them across the list of blank cells, the solver executes a breadth-first search to branch all legal boards until reaching a solution. Finally, the implementation is optimized by hoisting the repeated contention map calculation into a single precomputed operand passed down the call chain, allowing full 9×9 puzzles to be solved instantly.
## Atmosphere & style The presentation carries the calm, deliberate, and mesmerizing precision characteristic of classic APL demonstrations. John Scholes guides the viewer step by step through dense mathematical abstractions with a quiet, measured cadence, transforming complex array transformations into seemingly effortless one-liners. Watching it feels like observing a master craftsman at a digital workbench—intense, highly cerebral, and deeply satisfying as intricate grid logic crystallizes into a handful of glyphs.
## Bulletin - [00:00](https://www.youtube.com/watch?v=DmT80OseAGs&t=0s) A Sudoku solver is constructed in APL based on an algorithm by Arthur Whitney, demonstrated incrementally starting with a 4×4 board before scaling to full 9×9 puzzles. - [01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) Cell conflicts are modeled by joining row, column, and sub-box indices into coordinate triples, using outer product comparisons to generate a rank-4 boolean contention map (`cmap`). - [06:33](https://www.youtube.com/watch?v=DmT80OseAGs&t=393s) The solver employs a breadth-first search by identifying all empty cells (zeros) and folding a placement expansion function (`px`) over them across candidate board states. - [08:01](https://www.youtube.com/watch?v=DmT80OseAGs&t=481s) Precomputing the static contention map once at the top level and threading it down as an operand avoids thousands of redundant recalculations, enabling efficient solving of standard 9×9 puzzles.
## Key takeaways - [00:00](https://www.youtube.com/watch?v=DmT80OseAGs&t=0s) Sudoku grid geometry is generalized using sub-box dimensions of 2 for a 4×4 board and 3 for a 9×9 board. - [00:00](https://www.youtube.com/watch?v=DmT80OseAGs&t=0s) Each cell on the board is represented by a coordinate triple comprising its row, column, and sub-box index. - [00:00](https://www.youtube.com/watch?v=DmT80OseAGs&t=0s) A helper function named `box` generates sub-grid identifiers by shaping indices and replicating elements across rows and columns. - [01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) Contention between cells occurs whenever two cells share the same row, column, or sub-box index. - [01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) An outer product comparison across all coordinate triples yields a rank-4 array that encloses into a grid of boolean contention masks (`cmap`). - [01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) Candidate digits for any cell are determined by applying its contention mask to the puzzle and subtracting conflicting numbers from the universe of valid digits. - [01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) Empty cells are identified by testing the puzzle matrix for zero values. - [02:47](https://www.youtube.com/watch?v=DmT80OseAGs&t=167s) A placement operator uses double-alpha (`⍺⍺`) addressing to insert digits into specific cell coordinates without altering the rest of the board. - [02:47](https://www.youtube.com/watch?v=DmT80OseAGs&t=167s) The function `pvec` generates a vector of next-state boards corresponding to each valid candidate digit for a target cell. - [06:33](https://www.youtube.com/watch?v=DmT80OseAGs&t=393s) Filtering indexed puzzle coordinates with the zero mask produces an ordered vector of all empty cell coordinates needing assignment. - [06:33](https://www.youtube.com/watch?v=DmT80OseAGs&t=393s) Folding the placement operator `px` over all empty cells executes a breadth-first search across all legal board completions. - [08:01](https://www.youtube.com/watch?v=DmT80OseAGs&t=481s) The initial naive implementation suffered from severe performance degradation because `cmap` was recomputed on every candidate check within the search tree. - [08:01](https://www.youtube.com/watch?v=DmT80OseAGs&t=481s) Refactoring the solver to compute `cmap` once at the top level and pass it through operators via operand syntax (`⍺⍺`) eliminates redundant recalculations. - [08:01](https://www.youtube.com/watch?v=DmT80OseAGs&t=481s) With the contention map precomputed, the concise APL solver scales directly to solve standard 9×9 Sudoku puzzles.
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=DmT80OseAGs&t=0s) Building Box Numbers and Row-Column-Box Coordinates
The construction of the Sudoku solver begins by formulating how grid coordinates and sub-grid "box" regions are identified. To demonstrate the mathematics cleanly before tackling a standard 9×9 grid, operations are demonstrated on a smaller 4×4 Sudoku puzzle. In a 9×9 grid, the boxes are 3×3, while in a 4×4 puzzle, the sub-boxes are 2×2. The narrator starts by defining a function to generate box indices using the indices of a 3-element vector, shaping omega to the power of two, reshaping into a 3-row, 3-column matrix, and replicating items across both rows and columns. This yields a helper named `box` that calculates the sub-grid box numbers for any cell.
To establish the coordinates across the board, the row and column indices of the 4×4 array are paired, and each cell coordinate is joined with its corresponding box number. Here, `box` computes the region identifier by taking the square root of the first item of omega (`Omega to the power a half`). Applying this across the entire grid produces a 3-element descriptor—row, column, and box number—for every individual cell in the puzzle.
```mermaid flowchart LR accTitle: Cell Coordinate Construction accDescr: Process of building row, column, and box coordinate triples for a Sudoku grid. subgraph Grid Setup A["Grid Dimensions"] --> B["Row & Column Indices"] A --> C["Sub-box Partitioning"] end subgraph Coordinate Assembly B --> D["Cell Coordinate Triple (Row, Col, Box)"] C --> D end ```
- In a 9×9 puzzle, the sub-box dimension is 3, whereas in a 4×4 puzzle, the sub-box dimension is 2. - The coordinate structure associates each cell with a 3-tuple `(Row, Column, Box)`.
---
[01:17](https://www.youtube.com/watch?v=DmT80OseAGs&t=77s) Calculating the Contention Map and Available Digits
With every cell indexed by its row, column, and box numbers, the next step is determining cell contention. Two cells cannot share the same value if they sit in the same row, column, or box. Comparing an individual cell's coordinate triple (such as `3 3 4`) itemwise with other cells reveals a conflict: “There is contention where one is a member of each cell.” By extending this logic across the entire board using an outer product comparison across all cells, the system produces a rank-4 array. Enclosing the first two axes reduces this structure to a 4×4 map containing 4×4 boolean contention matrices. This function is named `cmap` (contention map).
To demonstrate how the contention map operates, an example 4×4 puzzle is loaded with blank squares represented by zeros. Selecting a specific cell, such as the third row and second column (cell `3 2`), retrieves its corresponding contention mask. When this boolean mask is applied back to the puzzle, it extracts all numbers currently occupying the same row, column, or 2×2 box as that cell. Subtracting these conflicting values from the set of all possible numbers (1 to 4 for a 4×4 board) isolates the exact candidate numbers available to be legally placed into that cell.
- A cell conflicts with any other cell sharing an identical row, column, or box index. - Outer product comparison produces a rank-4 array, which encloses to a 4×4 grid of boolean contention masks. - Zeros represent empty squares in the board representations.
---
[02:47](https://www.youtube.com/watch?v=DmT80OseAGs&t=167s) Placing Digits and Generating Candidate Placements
Building on the candidate finder, the algorithm develops an operator to place a chosen candidate digit into a target cell. Using left and right operands, an inner operand receives the target coordinates (such as `3 2`), addressed via double-alpha (`⍺⍺`). Using a take operation based on puzzle dimensions combined with an additive assignment mask, the operator places a value into the board array without disturbing the remaining cells. For instance, testing the operator allows placing a value like `99` directly at cell `(1, 1)`.
To advance board states during solving, the solver needs to generate all valid next-state boards for a specific cell. Given an empty cell at coordinates `(1, 1)`, the candidate function identifies which numbers can legally be played there. The placement function `pvec` is then mapped over each available candidate number, generating a vector of updated boards—one board for every candidate choice. Similarly, binding coordinates `(2, 2)` produces a series of board placements for that cell. This logic is abstracted into functions that take a cell coordinate, evaluate valid digits, and output the resulting list of populated board states.
- Target cell insertion is parameterized as an operator using double-alpha (`⍺⍺`) addressing. - Function `pvec` generates a vector of updated board configurations corresponding to each candidate digit for a single cell.
---
[06:33](https://www.youtube.com/watch?v=DmT80OseAGs&t=393s) Finding Empty Cells and Folding Over Placements
To systematically solve the whole puzzle, the solver must locate all unsolved cells and iteratively test valid placements across them. By inspecting the puzzle matrix, a boolean mask identifies all locations containing zeros. Multiplying this mask against the grid index matrix and filtering out zero entries isolates a vector containing the coordinates of every blank cell in the puzzle.
With the blank cells identified, the solver applies a reduction fold. A higher-order function, `px`, maps candidate placements for a cell across an entire list of candidate boards, expanding each current partial board into multiple new possibilities. Starting with the original unfilled board, folding `px` over the list of empty cells systematically branches through the possibilities: “Using all of the empty cells we have a solution vector for the puzzle s44.” For the sample 4×4 puzzle, this reduction succeeds in producing the completed solution vector.
```mermaid flowchart TD accTitle: Sudoku Breadth-First Expansion Flow accDescr: Flowchart showing how empty cells are extracted and folded over placements to generate solutions. subgraph Discovery A["Puzzle Matrix"] --> B["Detect Zero Cells"] B --> C["Vector of Empty Cell Coordinates"] end subgraph Expansion C --> D["Fold Placement Operator (px)"] D --> E["Branch Candidate Boards"] E --> F["Solution Vector"] end ```
- Blank cells are discovered by matching zero entries and compressing the index vector. - Folding the placement expansion over every empty cell performs a breadth-first search across all legal board completions.
---
[08:01](https://www.youtube.com/watch?v=DmT80OseAGs&t=481s) Optimizing Performance and Solving a 9x9 Sudoku
While the initial implementation correctly solves the 4×4 puzzle, the narrator highlights a significant architectural flaw: “However this coding is inefficient.” When `svec` folds `px` across empty cells, `px` calls `pvec` for every placement, `pvec` calls `avl` for every available number, and `avl` recalculates the exact same contention map (`cmap`) from scratch on every single invocation. Because the board geometry and cell relationships never change during the solve, recomputing the contention map thousands of times creates massive redundant overhead.
To fix this bottleneck, the code is refactored so that `svec` calculates `cmap` exactly once at the top level. The precomputed map is then passed down as an inner operand (`⍺⍺`) into `px`, forwarded as an inner operand (`⍺⍺`) into `pvec`, and finally received as `⍺⍺` inside `avl`. With this optimization in place, the algorithm avoids repeated grid analysis while retaining identical logic. The narrator applies the refined solver to a full 9×9 Sudoku puzzle, immediately producing the resolved grid. The presentation closes by displaying the concise, final APL code and encouraging viewers: “Try it for yourself at www.TryAPL.org.”
- Redundant calculation of `cmap` inside `avl` causes severe performance degradation during board search. - Precomputing `cmap` once in `svec` and passing it down via the operand syntax (`⍺⍺`) eliminates redundant work. - The completed solver operates seamlessly on standard 9×9 puzzles using Arthur Whitney's breadth-first search formulation.
## Mentioned APL, Arthur Whitney, Dyalog, Dyalog APL, John Scholes, Sudoku, The APL Challenge, The APL Forge, TryAPL.org
## Video description Development of a Sudoku solver in APL. You might want to hit pause now and again. Experiment for yourself at http://www.TryAPL.org. Notes on the algorithm: http://dfns.dyalog.com/n_sudoku_bfs.htm
Algorithm: Arthur Whitney Performance: John Scholes
--------------------
GET STARTED
Download the latest version of Dyalog: https://www.dyalog.com/downloads
TAKE PART IN OUR APL COMPETITIONS
THE APL CHALLENGE Learn APL and compete for one of three $100 prizes. Open to all experience levels. https://challenge.dyalog.com
THE APL FORGE Got a problem to solve or a library to share? Use Dyalog APL to build something great — and win £2,500 + an expenses-paid trip to present at a Dyalog user meeting. https://forge.dyalog.com
--------------------
[Source](https://www.youtube.com/watch?v=DmT80OseAGs)