View: Multi-Page Single Page

Simple Connect

Two verified puzzle engines — draw the pairs, or rotate the pipes.

Complete documentation, on one page.

v1.0 · by Living Failure

Overview

Simple Connect generates grid puzzles whose solution links every pair of matching endpoints with paths that never cross. By default the paths fill the whole board; an optional Strict lines rule keeps each path from running beside itself. It verifies them too — a solver reports whether a board has exactly one solution. Square & hex grids, deterministic from a seed, stored as ScriptableObjects. Pairs carry an Id, not a color. Pair boards drive two play styles: draw the lines, or play them as scrambled rotate-to-fix pipe tiles.

The second engine generates networks (square-only): one connected pipe system — straights, corners, Ts, crosses — the player rotates back together, proven unique by rotation. Shape it with branchiness and junction-bias dials, weld loops, cap dead ends down to zero for sealed plumbing, and pin fountain cells that must be fed by an exact number of pipes (2/3/4, or ? = rolled per board).

Requirements

  • Unity 6000.3+.
  • Core (generation / solving / rules / data / ScriptableObjects): no package dependencies, any render pipeline.
  • Demo: Input System + TextMeshPro; Built-in pipeline (URP/HDRP users convert the demo materials).
Assembly definitionsCore, Demo, and Editor are separate asmdefs, so the generator never pulls in the demo's packages.

Quick start

No code

Pairs: open Window ▸ Living Failure ▸ Simple Connect ▸ Connect Generator, pick Shape / size / pairs / coverage (and optionally Strict lines), click Generate, then Save Current or Save Pack.

Networks: open … ▸ Network Generator, dial in Branchiness / Junction bias (optionally Allow loops / Limit dead ends), click cells in the Fountains foldout to pin their pipe count (·234?) or set Random fountains, Generate, Save.

Code

using SimpleConnect;

var settings = new GenerationSettings {
    Width = 7, Height = 7, Shape = GridShape.Hex,
    PairCount = 0, TargetCoverage = 0.9f, RequireUnique = true,
    // Fill defaults to CoverAll; set Fill = FillRule.Connect for Strict lines.
};
ConnectPuzzleData puzzle = ConnectGenerator.GenerateVerified(settings);
List<ConnectPuzzleData> pack = ConnectGenerator.GenerateBatch(settings, 10);

// the network engine mirrors the same entry points
var net = new NetworkGenerationSettings {
    Width = 9, Height = 9, Coverage = 0.9f,
    Branchiness = 0.5f, DegreeBias = 0.4f, RandomFountains = 2,
    // AllowLoops = true, MaxDeadEnds = 0  (sealed plumbing)
};
net.Fountains.Add(new Fountain(4, 4, 4));   // pin the centre: exactly 4 pipes in
NetworkPuzzleData network = NetworkGenerator.GenerateVerified(net);
// network.IsUnique, network.Difficulty, network.FountainsSatisfied()

Playing

// pipes[i] = the path drawn for pair i, from its A endpoint
bool ok  = ConnectRules.IsLegalStep(puzzle, pipes, pairId, nextCell);
bool won = ConnectRules.IsSolved(puzzle, pipes);

Generating puzzles

The Generate window: Batch count, Clear on generate, Presets / Reset, Require unique, and Save Current / Save Pack.

By default the lines fill the board and may run beside themselves; Strict lines forbids self-touch (clean routes, some cells left empty). Square is 4-neighbor, Hex is pointy-top 6-neighbor (odd-r); both are topology-aware. Leftover cells become walls on purpose — that's what forces a unique solution. Generation is deterministic per seed — share the seed instead of the data.

Saved assets get inspector tools: a solution-count + difficulty badge plus Verify (re-check; Verify All reports any changed verdicts) and Re-roll (regenerate a slot until it verifies, then replace it; Re-roll Unverified fixes a whole pack at once). Both are undoable.

The Network Generator

Branchiness shapes the network (0 = corridors, 1 = bushy); Junction bias pumps T/cross density up to the structural ceiling (~⅓ of cells). Allow loops welds redundant routes; Limit dead ends seals the degree-1 tips — 0 = fully closed plumbing. Most looped/sealed boards still verify unique; the rest are honestly stamped Multiple (generation prefers unique ones).

Fountains: click cells in the window's Fountains foldout to pin an exact pipe count — the cycle is capped by what a cell can physically take (corner 2, edge 3). ? pins get a rolled 2–4 per board; Random fountains scatters N seed-deterministic pins. Pins are satisfied during growth and gate verification. Pinned cells show a blue frame in previews with an "all satisfied" row; orange dots are ordinary dead-end terminals, not fountains.

Pins persist between sessions — a warning shows above Generate whenever fountains are active, so they don't sneak into saved packs.

For runtime generation behind a loading screen:

var levels = await ConnectGenerator.GenerateBatchAsync(settings, 20,
    new System.Progress<int>(done => bar.value = done / 20f));

var nets = await NetworkGenerator.GenerateBatchAsync(netSettings, 20,
    new System.Progress<int>(done => bar.value = done / 20f));

Settings

FieldRangeMeaning
Width / Height3–20Grid size.
ShapeSquare / Hex4- or 6-neighbor.
Strict lines (Fill)off / onFill the board vs no-self-touch, gaps allowed.
TargetCoverage0.4–1.0How full the board is; rest are walls.
PairCount0…cells/2Pairs to place; 0 = auto.
MinPairLength2…Reject trivially short lines.
MinSeparation0…W+HMin gap between a pair's endpoints; 0 = auto.
RequireUniqueboolVerify a single-solution board.
TargetDifficulty0–5Roll until this star rating (0 = rank by quality).
SeedstringEmpty = random; reproduces the board.
UniquenessNodeBudget / UniquenessSeconds1k–5M / 0.2–20Solver node + time budgets.

Network settings (NetworkGenerationSettings)

FieldRangeMeaning
Width / Height2–30Grid size (square only).
Coverage0.4–1.0Network coverage; rest are walls/holes.
Branchiness0–1Corridors ↔ bushy (default 0.35).
DegreeBias0–1Junction appetite (default 0 = off).
AllowLoops / LoopCountbool / 0–99Weld cycles; 0 = auto (~1 per 20 cells).
MaxDeadEnds-1 / 0–99-1 = off; N = seal tips down to N; 0 = fully sealed.
Fountains / RandomFountainslist / 0–12Authored pins; extra random pins per board.
MaxAttempts1–500Boards tried for a verified, fountain-satisfied one (default 40).
SeedstringEmpty = random; reproduces the board.

API reference

ConnectGenerator

MemberReturns
Generate(settings)ConnectPuzzleData (unverified)
GenerateVerified(settings)ConnectPuzzleData (uniqueness-checked)
GenerateVerifiedAsync(settings)Task<ConnectPuzzleData>
GenerateBatch(settings, count)List<ConnectPuzzleData>
GenerateBatchAsync(settings, count, progress)Task<List<ConnectPuzzleData>>

ConnectRules

MemberNotes
IsLegalStep(puzzle, pipes, pairId, next)In bounds, not a wall, adjacent to the head, no crossing; Strict forbids self-touch.
IsSolved(puzzle, pipes)Every pair joined, no overlaps, board covered when required.
Validate(puzzle, out error)Well-formed data → bool.

Solver

Solver.Verify(puzzle)SolutionVerdict (Count, Verified, IsUnique, Found, Nodes). Bounded by node + time budgets.

NetworkGenerator

Mirrors ConnectGenerator's five entry points (Generate / GenerateVerified[Async] / GenerateBatch[Async]) over NetworkGenerationSettingsNetworkPuzzleData. GenerateVerified retries until a board is unique and every fountain is satisfied; raw Generate is one roll (check FountainsSatisfied() yourself). Settings objects are never mutated.

NetworkSolver & data

NetworkSolver.IsUnique(p) / CountSolutions(p, cap) — a solution is a rotation assignment with every edge reciprocated, nothing off-board or into a wall, and one connected network (validated against a brute-force oracle, loops included). NetworkDifficulty.Stars(p) rates 1–5. NetworkPuzzleData stores a per-cell connection mask (bits N=0 E=1 S=2 W=3, row-major; walls 0) — read with MaskAt / DegreeAt; PipeMask has the bit helpers (Bit / Opp / RotateCW / Degree). Fountain = Cell + RequiredDegree (1–4, or Fountain.RandomDegree = 0 for ?).

Types

ConnectPuzzleData (size, shape, fill, pairs, walls, verdict, difficulty) · EndpointPair (Id, A, B, Path) · GenerationSettings · Cell (X, Y) · GridShape · FillRule · SolutionCount · ConnectPuzzle / ConnectPuzzlePack (both IPuzzleProvider) · NetworkPuzzle / NetworkPuzzlePack (both INetworkProvider).

Reading a saved puzzle

Assign a ConnectPuzzle or ConnectPuzzlePack, then walk the data:

ConnectPuzzleData p = single.Puzzle;         // or pack.Get(0) / foreach pack.Puzzles

foreach (EndpointPair pair in p.Pairs) {
    int id = pair.Id;                        // map to YOUR color/pipe/number
    Cell a = pair.A, b = pair.B;             // endpoints
    foreach (Cell c in pair.Path) Draw(c.X, c.Y, id);   // intended route A..B
}
foreach (Cell wall in p.Walls) DrawWall(wall.X, wall.Y);
bool unique = p.IsUnique; int stars = p.Difficulty;   // verdict / rating

// networks: masks straight off the data
NetworkPuzzleData n = networkPack.Get(0);
int mask = n.MaskAt(x, y);      // solved tile's N/E/S/W bits; 0 = wall
int arms = n.DegreeAt(x, y);    // 1 endpoint .. 4 cross
foreach (Fountain f in n.Fountains) Mark(f.Cell, f.RequiredDegree);
Color is yoursThe data stores only pair Id — never a color. Map it in your renderer. For rotate play, scramble each tile's rotation and let the player turn them back — the demo's NetworkGame shows how.

The demo

The demo is a ready-to-play scene in Assets/SimpleConnect/Scenes/ with all three modes — open it and press Play. The landing menu picks Connect (then Color or Pipes style, Square / Hex, Strict lines) or Network (square-only, straight to source), then Random (generated, with a progress bar) or Puzzle Packs.

Draw controls: Square WASD/arrows · Hex QE AD ZC · drag to draw · Tab/19 pick the active pair · Backspace undo · R reset · H hide legend · F auto-solve. Rotate modes: click a tile to rotate · R fresh board · F auto-solve.

Skin it by dropping your tile prefabs on LevelRenderer3D (empty slots fall back to primitives); color comes from the pair Id. The rotate boards use the included pipe meshes (slots on RotateRenderer3D / NetworkRenderer3D); the Network board's generation dials sit on its NetworkController, and its Fountain Markers toggle (off by default) highlights pinned cells. One-click builders to rebuild it live in Editor/Tools but are kept off the menu — uncomment a builder's [MenuItem] line to bring it back under … ▸ Demo Setup (Create Demo (all modes) builds boards, HUD, and menu in one click).

Troubleshooting

  • "Not verified" — solver hit its budget; the board still plays. Use smaller grids / Require Unique / a higher budget.
  • Can't draw onto an empty cell — Strict lines blocks running beside your own line (allowed with Strict off), and you can only extend the active pair's head. Strict mode also leaves some cells empty on purpose.
  • Switch pairs — Tab / Shift+Tab or 1–9; or click an endpoint / existing line.
  • Magenta materials — Built-in shader in a URP/HDRP project; run Unity's material converter on the demo materials.
  • No sound — add an AudioListener to the camera.
  • Compile errors — install Input System + TextMeshPro; import TMP essentials.
  • Mouse won't draw — hold and drag over adjacent cells.
  • Hex keys — Q E A D Z C, not WASD (no straight-up neighbor on hex).
  • F does nothing — it auto-solves; ignored on a solved level; only R cancels mid-playback.
  • Network says "Multiple" — loops / dead-end sealing can rarely produce multi-solution boards; generation prefers unique and stamps honestly. Raise MaxAttempts or use Re-roll ▸ Multiple-solution.
  • Fountains "NOT satisfied" — the pin is statically impossible (corner caps at 2, edge at 3, conflicting duplicates) or the attempt budget ran out; spread pins or raise MaxAttempts.
  • Unexpected fountains / blue discs — pins and the Random fountains slider persist between sessions (watch the warning above Generate); the demo's discs come from NetworkRenderer3D's Fountain Markers toggle, off by default.

About

Simple Connect v1.0, by Living Failure. Verify don't hope; no color in the data; no bloat; pipeline-agnostic; readable example code. Also from Living Failure: Simple Hamiltonian, a cover-all / single-path generator. Support: livingfailuregames@gmail.com. Distributed under the Unity Asset Store EULA.