Contents

Solving Pips puzzles with MiniZinc

Posted on October 13, 2025 by Daniel Vitek.
Tagged: , .
The New York Times released their latest online game, Pips, a little over a month ago. Continuing a theme of using way too much compute on NYT puzzles, I decided to learn how to use an SMT solver with the goal of automatically solving Pips puzzles. While doing preparatory research for this post, I found that Joe Kerrigan had already published a nice writeup of how to do this in Z3, so I figured that for novelty’s sake I should try out MiniZinc. For bonus novelty points, our approach to setting up the constraints is going to be dramatically different from, and perhaps slightly better than, the approach in that blog post.

The anatomy of a Pips puzzle

A Pips puzzle consists of the following data:

The constraints come in five possible forms:

Note that the regions RiR_i do not necessarily cover
Alternatively, we could require that the regions cover BB but allow regions with trivially-true constraints.
BB, and puzzles don’t necessarily have unique solutions.

Prior art

The idea behind the approach in Joe Kerrigan’s post is to have two two-dimensional arrays of decision variables tile_id[r,c] and pip_count[r,c]. Each domino is treated as being composed of two tiles, with domino i corresponding to tiles 2*i and 2*i+1. It is relatively easy to write down constraints that ensure that tile 2*i has tile 2*i+1 as one of its neighbors, and that every tile appears exactly once. Further constraints ensure that the tile_id and pip_count arrays match up (i.e., that whatever value the tile ID at (r,c) prescribes is actually the value in pip_count[r,c]). Then the region constraints are straightforwards to write down in terms of pip_count.

Starting centers

The main idea behind our approach is to take advantage of the underlying checkerboard coloring of the board BB: we know that, writing Bi={(x,y)B:x+yimod2},B_i = \{(x, y) \in B: x+y \equiv i\bmod{2}\}, we have B0=B1|B_0| = |B_1| and that every domino covers one point in B0B_0 and one point in B1B_1. So we’ll arbitrarily call the points in B0B_0 the centers of the dominoes, and our solution will be described by three decision variables associated to each of the n_dom centers:

We’ll also want a 1D array of derived decision variables cell_pips (of size n_cell = 2*n_dom) corresponding to the pip values of all of the cells of the puzzle. This array is said to be derived because we can compute it from the three decision variables above using a system of constraints.

Supply constraints

Our remaining constraints then become quite natural:

We also add symmetry-breaking constraints on the domino_flip booleans for dominoes that have the same number of pips on both tiles. Note that the constraints on sums are the only constraints that make an SMT solver more appropriate than a SAT solver.

Obstruction theory

(Disclaimer: I am very unfamiliar with SAT/SMT solvers; the heuristics in this section all be totally wrong.)

One worrying aspect of both approaches is that there are “non-local” constraints on the tile_id/domino_dir variables. To see this, suppose that we have a configuration like the following:
A potentially-problematic configuration.

Suppose that the SMT solver starts by (in Joe Kerrigan’s approach) guessing the tile_id value of the darkened square, then using the sibling value for the square above or (in our approach, further supposing that the darkened square is a center) guessing the domino_id value of the darkened square and guessing its domino_dir value to be NORTH. What will happen under these circumstances? It is possible that the solver is able to make quite a bit of progress finding partial solutions to the remainder of the puzzle, and spends quite a lot of time doing so. (This seems like it would be even more of a problem for Pips puzzles with large numbers of solutions; most hard Pips puzzles have a small number of solutions.) But all this effort is in vain, for the initial choice has created a disconnected region that is obviously not tileable by dominoes.

It’s not clear how significant of a problem this is, or how to address it if it is in fact a significant problem. Perhaps some heuristics on which variables to guess first would help?

The thief of joy

I went ahead and reimplemented Joe Kerrigan’s grid-based approach in MiniZinc in order to compare performance against our center-based approach, then ran the two models on my laptop against all of the hard puzzles from August 18 through September 28.
A plot of performance ratios, with solve-time comparisons below.

The plot above shows the sorted performance ratios between the grid and center approaches. The median ratio is around 1.5, and two-thirds of the ratios are north of one, indicating that our center-based approach is usually a modest improvement. More notable is the second-from-right puzzle in the above plot, which corresponds to the unusually-difficult puzzle from September 15, 2025 shown below:
The September 15, 2025 board (via thewordfinder.com).

There turn out to be a very large number of solutions to this puzzle, and the enormous sum-equal-to region obviously generates a large arithmetic clause. The center-based model solved this puzzle in around 1.5 seconds on my laptop, while the grid-based model took around 35 seconds.

Try it yourself!

The code is up on my GitHub.

Digestif: a very confusing bug

I ran into a couple of bugs while coding this up (helped very much by Claude Code during both coding and debugging). The first was a relatively straightforward bug to hunt down: in the less-than/greater-than regions, Claude had interpreted the constraints as “every tile must have more/fewer than the target number of pips”. (The official rules are very clear on this distinction.) This faulty code generation was probably exacerbated by poor initial naming choices for the region types on my part. Thankfully, this bug was easy to track down, since several puzzles became unsatisfiable and a straightforward trace of failing clauses showed the error.

The second bug was a real doozy, however. I wanted to use lighter separators for the internal edges of dominoes so that it was obvious which way they were oriented. The current code uses ╪ (for horizontally-oriented dominoes) and ╫ (for vertically-oriented ones), but my original choice was : and ܅ (U+0705, a horizontal colon) to achieve a sort of dotted-line effect in terminal. My code had some bizarre output corruption where certain separator/junction characters were in the wrong spots, as in the image below:
Corrupted output of the Pips solver.

So, as one does, we can break down the output-formatting steps, and try turning various things on and off:

In more than 90% of cases the horizontal separators looked fine. But in going through the full archive of puzzle solutions, I noticed (after more time than I care to admit) a pattern: in all of the corrupted lines of horizontal separators, the entire line is reversed. At this point, Claude Code came up with the connection: the horizontal colon U+0705 lies in the Syriac block of Unicode, and Syriac, like most Semitic languages, is written right-to-left. I haven’t sorted out exactly what conditions cause a line consisting of various pipes and horizontal colons to be written right-to-left, but switching to separators that don’t have complicated text directionality completely sorted out the output corruption. I’m not sure when (if ever) I last had a bug whose root cause was so far outside of the space of hypotheses I was considering while debugging.