From constraint models to playable puzzle games

mzl1 pts0 comments

From constraint models to playable puzzle games - zayenz.seMenu

← Back to all blog posts← Newer<br>Older →

From constraint models to playable puzzle gamesDRAFT<br>2026-08-07•20 min read•Cite<br>•constraint programminggamespuzzle generationSudokuNonogramTentsLoopy

_p]:my-0<br>[&_li_>_ul]:my-2<br>[&_li_>_ol]:my-2<br>[&_a]:text-primary [&_a]:hover:underline [&_a]:font-medium<br>[&_blockquote]:max-w-[72ch] [&_blockquote]:border-l-4 [&_blockquote]:border-accent [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:text-muted-foreground [&_blockquote]:my-4<br>[&_pre]:bg-muted [&_pre]:p-4 [&_pre]:rounded-lg [&_pre]:overflow-x-auto [&_pre]:my-4<br>[&_pre_code]:bg-transparent [&_pre_code]:p-0<br>[&_code]:bg-muted [&_code]:text-foreground [&_code]:px-1.5 [&_code]:py-1 [&_code]:rounded<br>[&_hr]:border-t [&_hr]:border-accent [&_hr]:my-8<br>[&_section[data-footnotes]]:mt-12 [&_section[data-footnotes]]:pt-0<br>[&_section[data-footnotes]]:border-t [&_section[data-footnotes]]:border-accent/60<br>[&_section[data-footnotes]]:text-sm [&_section[data-footnotes]_ol]:mt-1<br>[&_section[data-footnotes]_p]:my-2<br>[&_strong]:font-bold<br>[&_em]:italic<br>[&_table]:w-full [&_table]:my-4 [&_table]:border-collapse<br>[&_th]:border [&_th]:border-accent [&_th]:p-2 [&_th]:text-left [&_th]:font-semibold<br>[&_td]:border [&_td]:border-accent [&_td]:p-2<br>[&_img]:rounded-lg [&_img]:my-4<br>">For my paper Scaling Sudoku as a Constraint Problem, I generated a repository of 434,201 Sudoku instances at five sizes between 6×6 and 36×36. I used them in constraint-programming experiments to ask which propagation scheme solves a puzzle without branching, how that changes with size, and how many clues move an instance from one hardness category to another.

I also wanted to play a few of them.

That small wish grew sideways. I now have playable versions of Sudoku, Nonogram, Queens, Zip, Loopy, Tents, Patches, Wend, and its Swedish sibling Swend. They are collected on a games page.

Each game section below includes a small model written in MiniZinc, a constraint-modelling language, and explains one part of the corresponding generator. The models are explanatory sketches rather than the programs that build the packs. Most generation and solving code uses Gecode 6.4.0. Additional programs handle importing, rasterisation, exact cover, and pack assembly.

What all nine games have in common is that I start with a solution or source image. I then add, move, or remove information until the intended answer is unique, or discard a candidate that cannot be repaired cleanly. Some of the same deductions later classify difficulty and provide hints.

Generation and uniqueness checking happen offline. The browser receives static puzzles and stored solutions; it does not run a solver. Difficulty labels come from propagation, search, or deterministic deduction measurements. They provide a relative, mechanically derived ordering within each pack; they do not estimate how difficult players will find the puzzles.

The listings were checked with MiniZinc 2.10.0. To keep them focused, they leave out input validation, search annotations, and the outer loop that rejects a candidate when a second solution exists. Output is omitted except where it defines which decisions constitute a puzzle solution.

Sudoku: from corpus to game#

Pencil marks in a 6×6 Sudoku<br>The Scaling Sudoku corpus begins with 32,000 uniquely solvable base puzzles at sizes 6×6, 9×9, 16×16, 25×25, and 36×36. A generator first creates a complete grid, then removes clues while preserving uniqueness. Gecode classifies the resulting puzzles.

The classification extends the setup in Helmut Simonis’s 2005 paper Sudoku as a Constraint Problem. It tries an ordered family of propagation configurations. Val, Bnd, and Dom refer to value, bounds, and domain propagation; BS and DS add bounds or domain shaving.1 The weakest successful configuration becomes the puzzle’s hardness tag. If none finishes without branching, the tag is Search.

The basic Sudoku model is pleasantly short. The box dimensions are data, which lets the same model handle 6×6 boards with 2×3 boxes and the usual 9×9 boards with 3×3 boxes.

sudoku.mzninclude "globals.mzn";

int: n;

int: box_height;

int: box_width;

set of int: N = 1..n;

set of int: Values = 1..n;

set of int: BoxTopRows = {

row | row in N where (row - 1) mod box_height = 0

};

set of int: BoxLeftColumns = {

column | column in N where (column - 1) mod box_width = 0

};

array[N, N] of 0..n: clue;

array[N, N] of var Values: board;

constraint forall (row in N) (

all_different(board[row,..])

);

constraint forall (column in N) (

all_different(board[..,column])

);

constraint forall (top in BoxTopRows, left in BoxLeftColumns) (

all_different(board[

top..top+box_height-1,

left..left+box_width-1

])

);

constraint forall (row, column in N where clue[row,column] > 0) (

board[row,column] = clue[row,column]

);

solve satisfy;

0) ( board[row,column] = clue[row,column]);solve satisfy;">

The playable pack is a reproducible,...

constraint border column sudoku data _blockquote

Related Articles