Contents

Overview

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.

Keyboard Shortcuts

The following keyboard shortcuts are available:

Grid Navigation & Input

Arrow keys Move selection in the grid
19 Enter a digit in selected cell
Backspace / 0 Clear selected cell
f Fill selected cells with sequential digits (1, 2, 3, ...)

Solving

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)

Code Sandbox

Ctrl+` Toggle sandbox panel
Ctrl+Enter / +Enter Run code (when sandbox is open)

History

Ctrl+z / +z Undo
Ctrl+Shift+z / +Shift+z Redo

Debug

Ctrl+d Toggle debug panel

Constraint Categories

All Constraints by Category

Recipes

Some constraint types aren't directly supported but can be constructed using combinations of available constraints.

Clone

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.

3x3 Magic Square

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.

Thermometer Variants

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

Nabner Line

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

Not Renban

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.

Arithmetic Progression

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;
}

Successor Arrow

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).*

Flatmates

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]*

Odd/Even Lots

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;
}

Sum Variants

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.

General Skyscraper

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
}

Ambiguous Arrow

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;
}

JavaScript Sandbox

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:

Sandbox Help Reference