MathOpt - Sudoku Generator
Problem: generate a filled Sudoku grid, remove clues, and keep only removals where a selected MathOpt backend can prove the puzzle has a unique solution.
Show code
import { initMathOpt, MathOpt } from 'or-tools-wasm/mathopt';
await initMathOpt();
function varIndex(row: number, col: number, digit: number) {
return (row * 9 + col) * 9 + (digit - 1);
}
const model = MathOpt.Model('sudoku');
const x = Array.from({ length: 9 * 9 * 9 }, (_, index) =>
model.addBinaryVariable({ name: `x_${index}` }),
);
const exactlyOne = (vars: typeof x) =>
model.addLinearConstraint({
lowerBound: 1,
upperBound: 1,
terms: vars.map((variable) => ({ variable, coefficient: 1 })),
});
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
exactlyOne(Array.from({ length: 9 }, (_, digit) => x[varIndex(row, col, digit + 1)]));
}
}
for (let row = 0; row < 9; row++) {
for (let digit = 1; digit <= 9; digit++) {
exactlyOne(Array.from({ length: 9 }, (_, col) => x[varIndex(row, col, digit)]));
}
}
for (let col = 0; col < 9; col++) {
for (let digit = 1; digit <= 9; digit++) {
exactlyOne(Array.from({ length: 9 }, (_, row) => x[varIndex(row, col, digit)]));
}
}
for (let boxRow = 0; boxRow < 3; boxRow++) {
for (let boxCol = 0; boxCol < 3; boxCol++) {
for (let digit = 1; digit <= 9; digit++) {
exactlyOne([0, 1, 2].flatMap((rowOffset) =>
[0, 1, 2].map((colOffset) =>
x[varIndex(boxRow * 3 + rowOffset, boxCol * 3 + colOffset, digit)]),
));
}
}
}
model.minimize([]);
const solverType = MathOpt.SolverType.CP_SAT;
const result = await MathOpt.solve(model, {
solverType,
threads: 4,
solutionLimit: 1,
cpSat: { numWorkers: 4, stopAfterFirstSolution: true },
});
console.log(result.terminationReason, result.variableValues);
Generate a Sudoku puzzle by solving a full MathOpt binary model with a selectable backend, removing clues one by one, and proving after each removal that the puzzle still has a unique solution.
Model
- Each row, column, cell, and 3x3 box is encoded with exactly-one constraints.
- Filled cells become fixed equality constraints in the MathOpt model.
- Generation first solves an empty grid to get a complete Sudoku solution.
- Then clues are removed one at a time only when a second solve proves uniqueness.
- The backend selector changes which MathOpt backend runs each Sudoku solve.
Status / Search:
Generate a puzzle to watch MathOpt remove clues.