A circuit board assembled by repeatedly committing the least-uncertain cell and letting the consequences propagate.
Tiles that agree at their edges
The board is a grid of cells, and each cell will end up holding one tile — a
chip, a connector, a lead, a diagonal lead, a clear square — in one of four rotations
and optionally mirrored.
Every tile carries four edge codes, one per side, and the only rule in the piece is
that two neighbours must agree on the edge they share:
typeNames.Add("CHIP");
typeConfigs.Add(new[] { 1, 1, 1, 1 });
A chip presents code 1 on all four sides; a connector presents 2 on one side and 0 on
the others. Traces cannot stop in mid-air because no tile offers a trace edge that
another tile can match with nothing.
Wave function collapse
Every cell starts holding all the states — every tile in every rotation. The solver
then repeats two moves:
// 1. find the cells with the fewest remaining options
if (e < minEntropy) { minEntropy = e; uncollapsed.Clear(); }
if (e == minEntropy) uncollapsed.Add(grid[i]);
// 2. commit one of them at random, then propagate
var cell = uncollapsed[rng.Next(uncollapsed.Count)];
cell.Collapse(rng);
Committing a cell removes options from its neighbours, which may remove options from
their neighbours, and the propagation queue runs until nothing changes. Then the
lowest-entropy cell is found again.
Choosing the most constrained cell rather than the next one in reading order is the
whole algorithm. It commits where there is least freedom, so contradictions are found
early rather than after half the board is laid.
A sixth of the way through the solve, drawn in the solver’s own order. Note that it is
not a wipe: committed cells appear in clusters, because each commitment constrains its
neighbours and drops their entropy below everyone else’s.
A third.
Half. The clusters are spreading along the traces, which is where the constraints are
tightest — a cell adjacent to a lead has far fewer legal options than one adjacent to
clear space.
Three quarters. What is left is the least-constrained region: open board, where almost
any tile still fits.
Solved.
Everything is a hatch line
Tone comes from hatching density rather than from fill. The six “colours” are hatch
specifications:
private static readonly float[][] CBlack = { new[] { 1f, 0.15f } };
private static readonly float[][] CDarkGrey = { new[] { 1f, 0.4f }, new[] { 1f + MathF.PI, 0.4f } };
private static readonly float[][] CWhite = Array.Empty<float[]>();
Each entry is an angle and a spacing; two entries means cross-hatching. White is an
empty array — no lines at all. Polygons are clipped against everything already drawn,
so this is hidden-line removal and the drawing could be plotted with a pen.
Five boards
| Rendering | Grid | Structure | Ink |
|---|
| Cyan Circuit Board | 70×70 | balanced components and traces | cyan on black |
| Lime Junction Array | 50×50 | coarse components, double traces, joints | electric lime on near-black |
| Amber Microgrid | 90×90 | fine, densely packed components | amber on warm black |
| Violet Diamond Network | 58×58 | inset diamond, every routing tile | soft violet on blue-black |
| Copper Trace Field | 38×38 | coarse chipless routing | copper on brown-black |
The tile set also varies: ChipOn, LeadDiagOn, LeadJointOn and the rest switch
whole tile types in and out of the vocabulary, changing what the solver can build
rather than only how much fits. BoardScale and BoardRotation make the violet board
an inset diamond, while the copper field removes chips entirely so routing becomes the
subject instead of the connective tissue.
Determinism
One Random is created per draw, seeded from the preset, and consumed twice per
collapse — once to choose among the tied lowest-entropy cells, once to choose a state.
The propagation consumes none of it. The generator is System.Random, which the
project’s determinism policy flags as not guaranteed stable across .NET versions.