Double Choco in the browser with a five-rung solver. The rule says a
block holds as many grey cells as cream ones and the grey half and the cream
half must be the same shape — turned or mirrored if you like. The clause
doing the work is the same shape, because a count can survive having a cell
moved and a shape cannot. Counted exhaustively over the 72 boards I ship:
958,977 connected sets have the right number of each shade, 83,225 of
them keep both halves in one piece (8.7%), and 34,800 have halves that are
actually congruent (41.8% of those, 3.6% of where we started). One sentence of
rule throws away 96.4% of the balanced sets. It also makes the theorem that
organises the weaker "equal counts" genre completely vacuous: of the 1,445
pairs of touching blocks in the shipped answers, 0 glue into a legal block,
where in the weaker genre 241 of them would each be a proof that the board
is ambiguous.
Live demo: https://sen.ltd/portfolio/double-choco/
Source: https://github.com/sen-ltd/double-choco
The rules
Every cell is printed grey or cream before you start. Your job is to cut
the grid into blocks.
- Each block holds the same number of grey cells and cream cells.
- Inside a block, the grey part is in one piece and the cream part is in one piece.
- And those two parts are congruent — the same shape, up to rotation and reflection.
- A number says how many cells of each shade its block holds. A block holds at most one number, and may hold none.
It is a relatively new Nikoli genre, invented in 2018 by a reader publishing
under the name Airplane. Deciding whether a shading admits any answer at all is
NP-complete (Đurić, 2022).
The third bullet is the whole genre.
A number constraint and a shape constraint are different animals
Every cut-the-grid puzzle I had built before this one — Shikaku, Araf,
Fillomino, Heyawake — grades a region by a number: its area, its cell count,
what its cells add up to. Numbers are forgiving. A region of six cells can be
lots of different six-cell shapes and still be a region of six cells.
Double Choco grades a region by a shape. Take a legal block and move one
cell from its grey half to its cream half: both counts are unchanged, and the
two halves are no longer the same polyomino, so the block is dead.
So the first thing worth measuring is how much of the search space that clause
throws away. Over the 72 shipped boards (36 at 6×6, 36 at 8×8), walk every
connected set of at most eight cells and sort it:
| board | balanced sets | halves in one piece | halves congruent | survives |
|---|---|---|---|---|
| 6×6 | 254,099 | 20,716 | 9,375 | 3.7% |
| 8×8 | 704,878 | 62,509 | 25,425 | 3.6% |
| total | 958,977 | 83,225 | 34,800 | 3.6% |
Of the sets that already have the right number of each shade, 91.3% die on
"each half must be in one piece" alone. Congruence then kills another 58.2%
of what is left. 3.6% survives.
And congruence is not a polite synonym for "looks similar". Only 34.7% of the
surviving blocks have halves that are plain translates of one another; the rest
genuinely need a turn or a mirror. Across the 774 blocks that shipped: 454 are
slides (58.7%), 299 need a rotation (38.6%), and 21 need a reflection that no
rotation can imitate (2.7%). The clause that says an L and a J are the same
shape earns its keep 21 times.
The experiment that isolates the shape clause
My usual way of testing how rigid an answer is: move one cell from its block
into a neighbouring block, and see what objects. Here it teaches you nothing.
All 5,377 such moves on the shipped boards are illegal, but 64.7% of them
are illegal purely because the two counts stopped matching. Arithmetic catches
everything before geometry gets a turn.
The sharp experiment is a swap: trade a cell of block A for a cell of block
B of the same shade. Every count on the board is unchanged. Every number still
has exactly the right number of cells under it. The only objections left are
geometric.
There are 17,860 such swaps available across the bank, and 0 are legal.
What objects:
| what objects to the swap | swaps it objects to | swaps it is the only objection to |
|---|---|---|
| a block falls into two pieces | 17,505 | 234 |
| one half falls into two pieces | 17,109 | 144 |
| the halves stop being the same shape | 1,570 | 83 |
| a number disagrees with its block | 367 | 6 |
| two numbers end up in one block | 319 | 21 |
83 rearrangements of a shipped answer keep every count, keep every block and
every half in one piece, keep every number happy — and are illegal anyway,
because an S is not an L. That is the shape clause saying something no other
rule on the board is able to say.
The theorem that organises the weaker genre is vacuous here
Drop the shape clause and Double Choco becomes "equal counts", a genre with a
neat little theorem:
Glue two touching blocks that hold no number. The union is connected,
still balanced, still numberless — so it is a legal different answer.
Therefore no two numberless blocks may touch in a puzzle with a unique answer.
That theorem is strong: it bounds the minimum clue count from below by a graph
invariant of the answer. I know it is strong because I wrote an implementation
around it first, having got the rules wrong and built the equal-counts variant
by mistake.
Add the shape clause and the glued block has to be congruent too — and it
essentially never is:
| board | touching pairs | unions that are legal blocks | unions legal without the shape clause | pairs with no number between them |
|---|---|---|---|---|
| 6×6 | 479 | 0 (0.0%) | 83 (17.3%) | 83 |
| 8×8 | 966 | 0 (0.0%) | 158 (16.4%) | 158 |
Not one of the 1,445 touching pairs in the shipped answers glues into a legal
block. 241 of those pairs carry no number between them — in the equal-counts
genre every single one would have been a proof that the board is ambiguous.
Which is exactly why boards in this genre can afford to stay quiet. Of the 774
shipped blocks, 349 (45.1%) hold no number at all. The shape clause is
already holding them in place, so the numbers do not have to.
Where I was wrong: existence
Going in, my reasoning was: existence is NP-complete, therefore random shadings
should mostly be dead boards. Measured, they are not.
| board | shading | drawn | has an answer | has one without the shape clause |
|---|---|---|---|---|
| 6×6 | uniform | 150 | 143 (95.3%) | 145 (96.7%) |
| 6×6 | blobby | 150 | 102 (68.0%) | 137 (91.3%) |
| 8×8 | uniform | 80 | 72 (90.0%) | 77 (96.3%) |
| 8×8 | blobby | 80 | 32 (40.0%) | 46 (57.5%) |
Uniformly random shadings cut up fine either way — 90.0% at 8×8 with the shape
clause, 96.3% without it. The clause bites on the blobby shadings, which is
to say the ones that look like a real published board: 40.0% against 57.5%. Big
single-shade areas are precisely where a block cannot find a congruent partner
nearby.
(The "without the shape clause" column is not 100% because blocks are capped at
four cells of each shade here, so the "the whole grid is one block" escape that
makes existence free in the equal-counts genre is not available either.)
Either way: what the clause destroys is not existence but abundance. Which
is why the generator cannot start from a shading. It lays the answer down
first — a shape plus a turn or mirror of itself, in raster order, one half grey
and one half cream — and lets the shading fall out of the partition as a
by-product.
The dial is the numbers, and it is a short dial
A block holds at most one number, so the total amount the numbers can ever say
is capped by the block count. Turn the dial the blunt way: put a number in m
blocks at random, in a random cell of each, and ask whether the answer is still
alone.
| numbers kept | 6×6 unique | 8×8 unique |
|---|---|---|
| 3 | 2/180 (1.1%) | 0/180 (0.0%) |
| 5 | 29/180 (16.1%) | 1/180 (0.6%) |
| 7 | 91/180 (50.6%) | 2/180 (1.1%) |
| 9 | — | 8/180 (4.4%) |
| 11 | — | 33/180 (18.3%) |
| 13 | — | 68/180 (37.8%) |
| one in every block | 30/36 (83.3%) | 29/36 (80.6%) |
The last row is the one to stare at. A number in every single block is not
enough. Six of 36 boards at 6×6, and seven of 36 at 8×8, still have a second
answer when every block is labelled with its own size. The numbers are a ceiling
as well as a dial, and the ceiling is low.
The shipped boards carry 4.3 numbers at 6×6 and 7.6 at 8×8, and every one of
them has been earned the hard way: take any single number away and a second
answer appears. npm test re-derives that from the shipped file rather than
trusting the generator.
Five rungs
The solver works on a catalogue, not on cells. catalogue() enumerates
every legal block of the board exactly once — each connected set is reached only
from its lowest-numbered cell, so nothing is found twice — and everything above
it is set cover.
| rung | what it knows |
|---|---|
only |
a cell that only one surviving block can cover belongs to that block |
bond |
intersect the blocks still available to a cell; any cell in that intersection shares its block whatever happens, so blocks holding one without the other are dead |
area |
every block is balanced and lies inside one connected run of unclaimed cells, so no run may be left with unequal shades |
probe |
assume a block, run the three rungs below, drop it if that alone contradicts |
search |
most-constrained cell first, over the same catalogue |
| rung | 6×6 cells settled | 8×8 cells settled | 6×6 finished | 8×8 finished |
|---|---|---|---|---|
only |
11.7% | 6.0% | 1/36 | 0/36 |
bond |
62.7% | 36.4% | 19/36 | 7/36 |
area |
81.9% | 53.1% | 29/36 | 17/36 |
probe |
100% | 100% | 36/36 | 36/36 |
Every board in the bank falls to probe without a single branch. Run the search
at area instead and the median board still takes 1 node at 6×6 and 3 at 8×8,
worst case 7 and 39.
area is the rung that notices a wall has strangled a corner, and it is where
most of the progress happens: 62.7% → 81.9% at 6×6.
Both ways of misreading the rule delete the puzzle, in opposite directions
The rule is usually written "each block holds the same number of grey and cream
cells, and the two areas are the same shape". There are three things to get
wrong, and they do not fail symmetrically.
| misreading | board | intended answer still legal | answers | still unique |
|---|---|---|---|---|
| drop the shape clause | 6×6 | 36/36 | median 9 (1 board past the 400 cap) | 3 |
| drop the shape clause | 8×8 | 36/36 | median 276 (14 boards past the cap) | 1 |
| let a half be in pieces | 6×6 | 36/36 | median 1, max 13 | 24 |
| let a half be in pieces | 8×8 | 36/36 | median 5, max 400 | 13 |
| read "same shape" as "same orientation" | 6×6 | 1/36 | 1/36 boards have any answer | 0 |
| read "same shape" as "same orientation" | 8×8 | 0/36 | 0/36 boards have any answer | 0 |
Loosen the rule and the intended answer stays perfectly legal — it just stops
being the only one, and nothing looks wrong until you count. Tighten it, by
forbidding the turn and the mirror, and the intended answer becomes illegal:
exactly one of the 72 boards has any answer at all under that reading, and the
rest go blank.
The loose misreading is the dangerous one, precisely because it is silent.
Counting, against numbers somebody else published
The shape engine carries every number on this page, so congruence is pinned to
the published polyomino counts three different ways:
| cells | fixed | A001168 | free | A000105 | one-sided | A000988 |
|---|---|---|---|---|---|---|
| 5 | 63 | 63 | 12 | 12 | 18 | 18 |
| 6 | 216 | 216 | 35 | 35 | 60 | 60 |
| 7 | 760 | 760 | 108 | 108 | 196 | 196 |
| 8 | 2,725 | 2,725 | 369 | 369 | 704 | 704 |
| 9 | 9,910 | 9,910 | 1,285 | 1,285 | 2,500 | 2,500 |
"Free" — up to rotation and reflection — is exactly the relation the rule
uses. Getting all three right is what shows the canonical form is not quietly
rotation-only or reflection-only.
The solver is pinned to a sequence with nothing to do with chocolate. Shade the
board like a chequerboard and cap every block at one cell of each shade: a block
is then one grey cell beside one cream cell, and two single cells are trivially
the same shape, so Double Choco is domino tiling.
| grid | solver | block-first brute force | published |
|---|---|---|---|
| 2×4 | 5 | 5 | 5 |
| 2×7 | 21 | 21 | 21 |
| 3×4 | 11 | 11 | 11 |
| 4×4 | 36 | 36 | 36 |
| 4×6 | 281 | 281 | 281 |
| 6×6 | 6,728 | — | 6,728 |
Fibonacci down the 2×n strips (A000045), then
A004003 / A099390.
And one count this repository had to work out for itself: how many block
shapes exist at each half size — an unordered pair of congruent polyominoes,
disjoint, with a connected union, counted up to translation. It is the size of
the vocabulary the genre is written in.
| cells of each shade | block shapes | halves only slide | distinct half shapes |
|---|---|---|---|
| 1 | 2 | 2 | 1 |
| 2 | 16 | 8 | 1 |
| 3 | 104 | 32 | 2 |
| 4 | 678 | 126 | 5 |
| 5 | 3,182 | 498 | 12 |
Searching the OEIS for 2, 16, 104, 678, 3182 in September 2026 returns nothing.
Three engines
The catalogue underwrites every number above, so it is checked against two
enumerators that have never heard of it. bruteByBlocks takes the lowest
unclaimed cell and grows every connected set out of it. bruteByBorders never
mentions a block at all: it walks the cells in raster order, decides the two
borders that arrive with each one, joins or cuts with a union-find, and only
checks the rules when a component can no longer grow.
All three agree on the answer set, not merely the count. The border engine
needed one correction to get there: two different border assignments can
describe the same partition — joining a cell to a neighbour it is already
connected to changes nothing — so its answers have to be collected as a set. A
failing test is how I found out, and it is a quiet trap in the border model
generally.
Of the 72 shipped boards, 72 carry a legal answer and 72 have exactly the answer
they shipped with. 27 tests.
Wrapping up
"Same count" and "same shape" are different kinds of constraint. A count can
survive having a cell moved; a shape cannot. The difference came out measurable:
of 958,977 connected sets with the counts right, 34,800 have the shapes right
too. Of 17,860 rearrangements that preserve every count on the board, 83 are
stopped by the shape clause and nothing else. And of 1,445 pairs of touching
blocks, 0 can be glued — where the weaker rule would have handed me 241
proofs that the board was ambiguous.
- Implementation: TypeScript + Vite, no runtime dependencies, 27 tests
- Generation: lay the answer down first, let the shading fall out of it, then strip numbers until none can go — every shipped board is a local minimum
- Verification: the complete solver agrees on the answer set with two naive engines; the shape engine reproduces A001168 / A000105 / A000988; a chequerboard capped at one of each reproduces the domino tilings
Demo and source at the top. Every number in this article comes out of
npm run stats / npm run ledger, and the README and the page body are
generated mechanically from that JSON.

Top comments (0)