A devlog by Neeraj Kotwani, who builds every game on Elipar.
Circuit Link asks you to route coloured wires between pairs of dots so that every square on the grid is covered. Ten boards, getting bigger. I generate them offline and ship the good ones.
"Good" means two things. A full-coverage solution has to exist, and it has to be the only one, because a board with forty valid answers is trivially easy. Almost any routing works and you're never wrong.
So the generator works backwards. Draw a random path that visits every square on the grid exactly once, chop it into contiguous pieces, and take the ends of each piece as a pair of dots. Do it that way and the pieces tile the grid perfectly by construction, so you know a solution exists before you've tested anything. Then hand the puzzle to a completely separate solver and ask how many answers it finds. Two or more, bin it.
Seven boards, then nothing
It produced the 5x5s and 6x6s in about a minute. Then it hit 7x7 and stopped. Not crashed, not hung. Just churned.
Two and a half minutes on 7x7 got me four candidate boards. Four. And the solver hadn't timed out once, which was the useful clue: if the solver isn't the slow part, the bottleneck is upstream, in the bit that makes the paths.
My first theory was that I'd written a bad depth-first search and it was thrashing. I spent a while reading it. The search was fine.
Colour the grid like a chessboard
Here's the actual reason, and I like it a lot more than a bad search.
Colour every square by whether row plus column is even or odd, so you get a chessboard. A path that steps only up, down, left or right has to alternate colours on every single step. Black, white, black, white. There's no way around it, it's just what moving one square does.
A 7x7 grid has 49 squares, which splits 25 and 24. If your path visits all 49 and alternates the whole way, it starts and ends on the class with 25. It cannot do anything else. And I was picking the starting square uniformly at random, so roughly half the time I was asking for a path that provably does not exist.
Worse, the search didn't know that. Each impossible request burned a full exhaustive hunt through the entire grid before giving up.
The fix is one condition: when the square count is odd, only start from the majority colour. Before, 80 attempts in 20 seconds yielded 40 paths. After, 57,245 attempts yielded 57,245 paths. Every single attempt succeeded, and it was about 1,400 times faster.
I'm recording this partly because the symptom is so misleading. A generator that gets slower as the grid grows looks exactly like an algorithmic complexity problem, and the instinct is to go optimise the search. If a grid-path generator of yours seems pathologically slow, count the squares and check the parity before you touch anything else.
Three smaller things that cost me more time than they should
The node budget is doing real work. I gave the solver a generous allowance per candidate, on the theory that a wall-clock deadline would catch runaways. It doesn't, because the deadline is only checked between candidates. One slow 7x7 board ran for minutes and the deadline never got a look in. Dropping the allowance to 40,000 nodes, so slow candidates get abandoned and more boards get tested, found unique 7x7 boards in seconds.
The hard case is the small number of wires, not the big grid. I assumed 7x7 was hard because it was 7x7. It's hard when it only has six wires, because fewer wires means longer paths, which means far more freedom in how you route them, which makes solving slow and uniqueness rare. Seven to ten wires on the same grid all solved in under six seconds each. The shipped difficulty ramp adds wires as boards grow, and that's why.
Write your output as you go. The first version printed its JSON at the end. I killed it after it had found seven perfectly good boards and threw all seven away. Version two appends each board to the file the moment it passes. This is not a clever insight, it's just annoying enough that I want it written down somewhere.
The bug that never shipped
One more, caught by an assertion rather than by playing. The palette had six colours and gets indexed with a modulo, but the final boards need nine wires. Wires seven, eight and nine came out looking identical to one, two and three.
That's not ugly, it's broken. Two wires the same colour makes the puzzle genuinely ambiguous to look at, no matter how unique the solution is on paper. All that careful uniqueness checking, undone by a short array.
I found it by asserting that the number of distinct colours is at least the maximum wire count on any board. Ten colours now, with a comment telling whoever adds a bigger board to extend it. Terminals also show their pair number, so colour is never the only thing distinguishing two wires.
Circuit Link is free in a browser, ten boards, no download. Every one of them has exactly one answer, which I can now say with some confidence.