File a bug
Source code
The Interactive Sudoku Solver (ISS) is a solver designed to quickly solve variant sudoku puzzles. It does not use human solving techniques, instead optimizing for speed.
Use it to find all the solutions, count the solutions, or see all the valid values for each cell in a puzzle.
Visit the github repository to view the source code and report bugs.
The following keyboard shortcuts are available:
| Arrow keys | Move selection in the grid |
| 1 – 9 | Enter a digit in selected cell |
| Backspace / 0 | Clear selected cell |
| f | Fill selected cells with sequential digits (1, 2, 3, ...) |
| Ctrl+Shift+Enter / ⌘+Shift+Enter | Solve |
| n | Next solution |
| p | Previous solution |
| s | Go to start |
| Alt+Click | Pin a value in a cell (Step-by-step mode) |
| Ctrl+` | Toggle sandbox panel |
| Ctrl+Enter / ⌘+Enter | Run code (when sandbox is open) |
| Ctrl+z / ⌘+z | Undo |
| Ctrl+Shift+z / ⌘+Shift+z | Redo |
| Ctrl+d | Toggle debug panel |
Some constraint types aren't directly supported but can be constructed using combinations of available constraints.
Description: Two regions of the same shape and size, which must have the same values in corresponding cells.
Use the Same Value Sets on every pair of corresponding
cells to mark them as equal. Each pair must be a separate constraint.
Description: A square of cells, where the sum of the values in each row, column, and diagonal is the same.
Use the Cage constraint on each row, column, and diagonal
to sum to 15.
Description: A thermometer is a line where values are strictly increasing. Some variants modify this basic rule.
Most Thermometer variants can be created with pairwise constraints in the "Custom JavaScript constraints" panel, with an appropriate condition.
For an Odd/Even Thermometer, where the values must be all odd or all even, use the following condition:
a < b && (a % 2 == b % 2)
For a Slow Thermometer, where the values don't need to be strictly increasing, use the following condition:
a <= b
Description: No two digits along the line may be be consecutive. ("Nabner" is "Renban" spelled backwards.)
Create a pairwise constraint in the "Custom JavaScript constraints" panel with "Chain handling" set to "All pairs" and use the following condition:
Math.abs(a - b) > 1
Description: Unlike Nabner, this is how to create a set where all the values taken together are not consecutive.
Implement this by configuring a state machine in the
"Custom JavaScript constraints" panel. The NFA tracks the minimum and
maximum values seen, accepting if max - min != count - 1
(i.e., the values are not all consecutive). If you know all the
values are distinct, then this is sufficient:
NUM_CELLS = 3; // Number of cells in the set
startState = { min: 16, max: -1 };
function transition(state, value) {
return {
min: Math.min(state.min, value),
max: Math.max(state.max, value),
};
}
function accept(state) {
return state.max - state.min !== NUM_CELLS - 1;
}
If the values may not be distinct, then you need to also need to check for duplicates. This can be done efficiently by using non-deterministic branches to check each digit:
NUM_CELLS = 3; // Number of cells in the set
startState = {type: 'start'};
function transition(state, value) {
if (state.type === 'start') {
return [
{ type: "rangeCheck", min: value, max: value },
{ type: "duplicateCheck", value },
];
}
if (state.type === 'rangeCheck') {
return [{
type: "rangeCheck",
min: Math.min(state.min, value),
max: Math.max(state.max, value),
}];
}
if (state.type === 'duplicateCheck') {
if (value === state.value) {
return [{ type: 'hasDuplicate' }];
} else {
return [
state,
{ type: 'duplicateCheck', value }
];
}
}
if (state.type === 'hasDuplicate') return [state];
}
function accept(state) {
if (state.type == 'rangeCheck') {
return state.max - state.min !== NUM_CELLS - 1;
}
return (state.type === 'hasDuplicate');
}
You can generalize this to a single state machine that works for any number of cells, but it becomes a larger and less efficient.
Description: Digits along the line must form an arithmetic progression. That is, the difference between all consecutive digits is same.
Implement this by configuring a state machine in the "Custom JavaScript constraints" panel:
startState = { lastVal: null, diff: null };
function transition(state, value) {
if (state.lastVal == null) {
return { lastVal: value, diff: null};
}
const diff = value - state.lastVal;
if (state.diff == null || state.diff == diff) {
return { lastVal: value, diff: diff };
}
}
function accept(state) {
return true;
}
Description:
If a digit N is placed in an arrow cell, the digit
N + 1 must appear exactly N cells away in the
arrow's direction.
Select the arrow cells and all the cells along the arrow's direction,
then add a Regex constraint from "Line and Sets" panel.
The regex pattern should alternate over each possible starting digit:
(12|2.3|3.{2}4|4.{3}5|5.{4}6|6.{5}7|7.{6}8|8.{7}9).*
Description:
Generalize the "Dutch Flatmates" constraint to apply to an arbitrary
center digit D, which must have either digit A
above it or digit B below it (or both).
Use a Regex constraint from the "Line and Sets" panel,
applied to every column (replace A, B and
D):
.*(AD|DB).*NOTE: This assumes that
D must exist in the line. This
might not be the case in non-standard grid. In that case,
then you can modify the regex as follows:
[^D]*(AD|DB)?[^D]*
Description: A digit in a marked cell indicates the number of Odd/Even digits which appear along the attached line, including itself.
Implement this by configuring a state machine in the
"Custom JavaScript constraints" panel. Select the cells in the line
so that the marked cell is first, the order of the rest don't matter.
The following code implements Odd Lots, but change to the
isEven function for Even Lots:
startState = { remaining: null };
function transition(state, value) {
const isOdd = x => x%2 == 1;
const isEven = x => x%2 == 0;
let remaining = state.remaining;
// If we are starting, then initialize the count of remaining values.
if (remaining == null) remaining = value;
// Update remaining count if we see a matching value.
if (isOdd(value)) remaining = remaining - 1;
// Only return a new state if it is valid.
if (remaining >= 0) return { remaining: remaining };
}
function accept(state) {
return state.remaining == 0;
}
Description:
A set of cells where the sum must satisfy a custom condition.
Use this when you need sum constraints beyond what the built-in
Cage supports — such as parity, ranges, or multiple
possible totals.
Implement this by configuring a state machine in the
"Custom JavaScript constraints" panel. Select the cells in the set.
The transition function accumulates the sum, and the
accept function defines your condition:
startState = 0;
function transition(state, value) {
// Limit avoids infinite states (max sum in a 9-cell region)
// Increase if you need to support larger regions
if (state <= 45) return state + value;
}
function accept(state) {
// Customize this condition (see examples below)
return state < 10;
}
Examples of accept conditions:
// Sum within a range return state >= 10 && state <= 20; // One of several possible sums return [15, 20, 25].includes(state); // Even sum return state % 2 === 0; // Odd sum return state % 2 === 1;
This allows duplicate values. If you need all values to be
distinct, overlay an All Different constraint from the
"Lines & Sets" panel on the same cells.
If you want the values to be restricted as well, then you can filter
out unwanted values in the transition function.
Description: Generalize the skyscraper constraint to apply along any line of cells (for example, a diagonal). From the start of the line, there is a target indicate how many "buildings" are visible, where taller buildings block the view of shorter ones behind them.
Implement this by configuring a state machine in the "Custom JavaScript constraints" panel. Change the `accept` function to set the target number of visible buildings:
startState = { max: 0, seen: 0 };
function transition(state, value) {
return {
max: Math.max(state.max, value),
seen: state.seen + (value > state.max),
}
}
function accept(state) {
return state.seen === 5; // Change to target number of visible buildings
}
Description: The greatest cell value must equal the sum of the remaining cells. When applied to a 2x2 square, this is also known as the Quad Sum constraint.
Implement this by configuring a state machine in the "Custom JavaScript constraints" panel. This checks that the total sum is twice the maximum value:
startState = { max: 0, sum: 0 };
function transition(state, value) {
if (state.sum > 18) return;
return {
max: Math.max(state.max, value),
sum: state.sum + value
}
}
function accept(state) {
return state.sum === 2*state.max;
}
The Sandbox API may change without notice.
The Sandbox API exposes internal solver details directly which were not originally designed for general use. There will be rough edges.
The JavaScript Sandbox can be used to create more complex puzzles by writing JavaScript code that programmatically creates constraints.
Open the sandbox by clicking on the [JavaScript Sandbox] link at the bottom of the page or by pressing Ctrl+` shortcut.
The sandbox provides access to all constraint types and utility functions for working with cell IDs. You can generate constraints dynamically, parse and modify existing constraints, or create constraints that would be tedious to add manually.
Run help() in the sandbox console to see the full
reference, or expand the section below: