Build a Network Puzzle: A Small Model, Validator and Test Plan
A network puzzle is a manageable programming project if you separate the board model from drawing it. The difficult part is not drawing a glowing pipe. It is defining what counts as a connection, checking a complete board correctly and keeping replay behavior understandable after a change. This guide uses design choices from this site's implementation and clearly separates existing behavior from proposed improvements.
Start with a nine-cell model
Use a flat array plus row and column counts. For a cell at row r and column c, its index is r multiplied by the column count plus c. Give directions four bits: north 1, east 2, south 4 and west 8. An east-west straight is 10; a south-west elbow is 12. Rotating a mask clockwise moves each bit to the next direction and wraps west back to north.
Before adding graphics, use this complete 3 by 3 board: [2, 10, 12, 6, 10, 9, 3, 10, 8]. It forms one nine-cell path with the middle cell as server. It contains no outward openings and has eight reciprocal edges. Unlike a disconnected fragment, this fixture lets you test both local matching and global reachability. The walkthrough explains the same pieces as a player would read them.
Make neighbor lookup an explicit policy
For a bounded board, a requested row outside zero through rows minus one has no neighbor. Do the same for columns. For a wrapping board, reduce each coordinate modulo its dimension. Keep this rule in one function rather than scattering edge exceptions through rotation, rendering and validation. A test near the top-left corner should therefore exercise a different neighbor set in the two modes.
When adding a connection, set both halves together. Opening east at one cell requires opening west at its neighbor. A one-sided arm is an invalid opening, not an edge. If your search follows one-sided arms, it can report false connectivity and accept a board that visibly contains gaps. Deliberately corrupt one arm in your fixture to make sure the validator notices.
Validate three separate properties
First count unmatched openings, including openings beyond a solid boundary. Then traverse reciprocal edges from the server and count reached cells. Finally, apply a cycle rule if the chosen mode requires a tree. A connected graph with V vertices and V minus one undirected edges is a tree, provided edges are counted once and the board representation has been validated.
Do not reduce completion to color or reachability alone. A network can reach every cell and still contain an extra loose arm. Conversely, two separate components can each have internally matched pipes. Make both situations explicit negative tests. Classic here permits extra reciprocal edges, so a no-cycle condition belongs to Bounded and Torus rather than to the shared completion predicate.
Generate a solution, then scramble
This implementation grows a randomized frontier tree across the neighbor graph. It records reciprocal arms, then rotates pieces to create the displayed puzzle. Classic may add extra edges and supply locked clues before scrambling the remaining tiles. Restoring the constructed orientations gives at least one solution, but this construction alone does not establish uniqueness or a particular human difficulty.
Inject the random source into every generation step, including clue selection and scrambling. If one step secretly calls a different random source, your supposedly seeded Daily board will diverge between runs. Reproducibility also depends on the algorithm version and random-call order. A future generator revision needs a replay policy rather than an assumption that an old date always reconstructs an old board.
Make Undo a model operation
Record enough information to reverse the player's move, then redraw from the model. Do not infer state by reading canvas pixels. Decide what happens to timers and move counters separately; the player should not need to guess which figures are restored. New Game and Undo must remain distinct actions, and controls should communicate that a random board will be replaced.
For persistence, choose exactly what you save. Here, dimensions and personal best move counts can persist in local storage; an unfinished random board lives in memory. A production implementation should tolerate denied or unavailable storage rather than preventing play. Remote rankings would require a different trust model because a browser can edit its own state; local scores should not be presented as verified competition results.
Test interaction and failure states before polish
Use fixed boards for logic tests and a range of seeds for generated-board properties. Check corners, wrapping seams, fully connected boards with a dangling arm, disconnected matched islands and mode-specific cycles. Exercise invalid dimensions at the input boundary. In a browser, test a small viewport, touch rotation, Undo, new-game confirmation behavior where provided, and the absence of page errors.
Do not hide instructional content behind JavaScript when it can be ordinary HTML. This site's tutorial supplies a static explanation and a button grid for keyboard interaction; its main canvas still lacks per-tile keyboard navigation. That is a known limitation, not a complete accessibility solution. A future implementation can reuse the same model with semantic buttons, but needs usability testing before claiming equivalence.
The next useful extension is an independent solution checker or a uniqueness search, not another visual effect. Those are future options: this site's runtime does not independently validate each newly generated board or reject every accidentally solved starting position. Read the generation guide before turning a construction guarantee into a stronger claim.