Yin-Yang (Shiromaru-Kuromaru) in the browser with four rule sets
inside. Color every cell of an n×n grid black or white. Two rules only:
each color forms one orthogonally connected group, and no 2×2
square is a single color. Puzzle #35 in the solver series.
Demo: https://sen.ltd/portfolio/yin-yang/
Repo: https://github.com/sen-ltd/yin-yang
I picked this one because the name is a theorem.
The name is a theorem
Read the frontier — the set of grid edges separating a black cell from a
white one — as a curve on the lattice points. The two rules translate
directly into properties of that curve:
- a monochrome 2×2 is an interior lattice point the curve misses: the four cells around a point agree exactly when no frontier edge touches it;
- a checkered 2×2 is a point where the curve crosses itself — and it's forbidden even though no rule says so: the diagonal black pair and the diagonal white pair would each need a connecting path, and two disjoint paths between opposite corners of a square cannot avoid crossing in a planar grid. Connectivity's local shadow;
- a second frontier component would cut the board into three or more monochrome regions, two of which share a color they cannot reach.
So in every legal board the frontier is a single non-crossing curve
through every interior lattice point — a Hamiltonian path on the
(n−1)×(n−1) interior grid, extended one edge to the border at each end.
Rarely it closes into a Hamiltonian cycle instead, and one color becomes
landlocked.
Sanity check at 2×2: one interior point, the curve just passes through it,
C(4,2) = 6 ways to pick its two edges, times two colorings — 12 boards.
Exhaustive enumeration also says 12. The 3×3 count is 34 (2 of them
landlocked: a lone stone in the center — the purest yin-yang symbols), the
4×4 count is 96. Six counting engines agree on all of these.
Across 1,500 generated boards: frontier components always 1, crossings
always 0, border color changes always 0 or 2. Zero exceptions — which is
why the demo's win message says so.
The generator runs the theorem backwards
The first generator was naive: place random colors on random cells,
propagate, backtrack on contradiction. 6×6 took 3ms per board. 10×10 took
5.6 seconds — scattered placements plant connectivity failures that only
surface deep in the search.
So use the theorem instead. Valid board ⟺ Hamiltonian path, so draw the
curve, not the coloring:
- start from a serpentine Hamiltonian path over the interior lattice;
- mix it with backbite moves (reattach an endpoint to a random neighbor and reverse the tail — the standard Markov chain for random Hamiltonian paths on grids);
- keep mixing until both endpoints sit on the interior's edge, extend each to the border;
- flood the two sides of the curve with the two colors.
Zero backtracking, 0.6ms per 10×10 board — about 9,000× faster. The
generator benefits from the theorem before the solver ever does.
Four rule sets
The solver whittles per-cell candidates cand[k] ∈ {black, white, both}.
| level | rule |
|---|---|
window |
enumerate the 12 legal colorings of a 2×2 (16 minus 2 monochrome minus 2 checkered) consistent with the current cells; fix what every survivor agrees on |
border |
the border cells form a cycle and each color may occupy at most one arc of it — decided border cells may never show more than 2 color changes around the ring |
bridge |
connectivity made local: placed cells of a color must stay mutually reachable through (that color ∪ free); a free cell sealed off from a color can't take it, a free cut vertex between two placed cells must. One Tarjan articulation-point pass per color, O(V+E) |
probe |
assume one color on one cell, run the rules below to a fixpoint, drop the assumption if that alone is a contradiction |
Only half of the window rule is stated in the puzzle. The checkered ban
and the border-arc rule are both cheap local shadows of the one expensive
global rule. The independent validator doesn't know about either — it
checks the two definitional rules only, and the shadows have to emerge.
Incremental vs ablation — they disagree again, harder than ever
Three sizes × 300 boards from the raw generator stream, unfiltered by
uniqueness or difficulty (so the numbers aren't shaped by the property they
measure). Clue density 0.8, fraction finished by the fixpoint alone:
| board | window | +border | +bridge | +probe | unique in the raw stream |
|---|---|---|---|---|---|
| 6×6 | 2.3% | 16.7% | 57.3% | 57.3% | 57.3% |
| 8×8 | 0.0% | 2.7% | 39.3% | 39.3% | 39.3% |
| 10×10 | 0.0% | 0.0% | 16.7% | 17.0% | 17.0% |
Ablation — full ladder minus one rule:
| board | full | −window | −border | −bridge |
|---|---|---|---|---|
| 6×6 | 57.3% | 15.3% | 57.3% | 16.7% |
| 8×8 | 39.3% | 2.3% | 39.3% | 2.7% |
| 10×10 | 17.0% | 0.0% | 17.0% | 0.0% |
Incrementally, border looks like a supporting actor: +14.4pt at 6×6.
Ablate it and nothing drops, at any size — window+bridge+probe catch
every board it would have caught. Remove window or bridge instead and
10×10 collapses to 0.0%. This series keeps finding incremental/ablation
disagreements; this is the most extreme one yet.
Border still earns its keep, though: it defines a real difficulty grade —
its bank boards (median 11 clues at 6×6) sit exactly between window's (20)
and bridge's (8) clue budgets — and it's the rule human solvers actually
play with. Redundant is not the same as useless, third time running.
Certifying uniqueness (solution count = 1) on 100 raw 10×10 boards needs a
median of 527 search guesses with window alone, 21 with border, 2 with
bridge. The moment connectivity enters the propagation, uniqueness
checking almost stops being a search.
Probe's solve rate = the uniqueness rate, again
The probe column equals the uniqueness column at every size. A sound
fixpoint cannot decide a cell that two answers disagree on, so the
uniqueness rate is a ceiling — and the ladder sits on it, at every size and
every density point below.
Yin-Yang is starving for clues
The density sweep, 10×10, 150 boards per point:
| density | unique | flip ceiling E[d^flips] | bridge-solved | window-solved |
|---|---|---|---|---|
| 0.50 | 0.0% | 80.0% | 0.0% | 0.0% |
| 0.70 | 2.7% | 88.5% | 2.7% | 0.0% |
| 0.80 | 16.7% | 91.1% | 16.7% | 0.0% |
| 0.90 | 60.7% | 95.1% | 60.7% | 1.3% |
| 0.95 | 88.7% | 97.5% | 88.7% | 8.7% |
| 1.00 | 100.0% | 100.0% | 100.0% | 100.0% |
Reveal 90% of the board at random and 4 boards in 10 still have more than
one answer. Far worse than Slant's threshold (50% unique at density 0.75).
To find out why, I counted flippable cells: cells whose lone color flip
keeps the whole board valid. Any flippable cell left unclued is a free
second solution, so P(unique) ≤ E[density^flips]. But random boards average
only 0.4 flippable cells at 10×10, so that ceiling is high — and the
measured curve sits far below it everywhere short of 1.0. The gap is the
finding: Yin-Yang's ambiguity lives in multi-cell reroutes of the curve,
not single-cell flips. Where the frontier crosses a clue-free region, many
paths fit, and one clue kills only one of them.
The shipped bank is thinned adversarially instead (start from the full
reveal, delete clues while the target level still finishes without
guessing): a probe-grade 10×10 board pins the answer with a median of 24
clues — certainty that a random reveal of 90 cells still fails to buy 40%
of the time. A clue is only worth something where it pins the curve.
Landlocked boards have a parity
Boards whose frontier closes into a loop — one color never touching the
border — exist, but they're rare and they carry a parity. Exhaustively:
exactly 2 at 3×3, none at 4×4 or 6×6 (full enumeration of all interior
subsets), and explicit constructions at 5×5 and 7×7 (they're in the tests).
On even boards the corner 2×2 windows pin interior stones into a monochrome
square. In 1,500 random configurations: zero closed curves.
Verification
Every claim is counted two independent ways: a propagating search at each
of the four rule levels, and a brute force that shares no code with any of
them — row-major DFS over free cells, pruned only by the two definitional
rules (a completed monochrome 2×2 is dead; placed cells of a color must
stay mutually reachable), every leaf scored by a standalone validator.
Wherever the brute force is feasible, all levels must agree on the solution
count: 360/360 (board, level) pairs.
Exact anchors: the clue-free 2×2 / 3×3 / 4×4 boards count 12 / 34 / 96,
and the frontier theorem is verified over all valid boards up to 4×4 —
not just generated ones, which would be circular, since the generator draws
boards from curves. All 33 tests.
Play it
The Hint button proves one cell at a time at the selected level; the rule
selector doubles as the difficulty dial, and the bank only serves boards
that first become guess-free at that level. The black–white frontier is
drawn live in purple — you can watch it join up into a single curve as the
board closes.
Next up: probably another puzzle with a solver inside.
SEN LLC — software development experiments, shipped in public.
More portfolio: https://sen.ltd/portfolio/

Top comments (0)