What it does
A level is a board of sockets in any layout and a set of ropes, each with two ends: a plug in a socket, or a fixed anchor, for ropes that hang from a bar. Where two ropes' paths cross, the one moved last lies on top. A move takes one end of a rope to a free socket, after which that rope lies over everything it crosses. The level is won when nothing crosses, when every rope's plugs sit on sockets of its colour, or both, as the level chooses.
Rule options change the game. With lock, a rope that has another lying over it cannot move, which makes the
over-and-under order matter. With clear, a rope that crosses nothing (and is home, when colours count) leaves the
board and frees its sockets. A move limit makes a level losable. Stars come from a par: three for matching it, two
within half again, one otherwise.
You can write levels by hand, or let generate make them by scrambling a solved board, so every level is solvable and
comes with its solution and par. The difficulty is set by the fewest moves the best solution may take and by the number
of crossings. search finds winning moves from any position, which is what hint() uses, and every game has undo,
redo and saves. For feel, createUntangleFeel gives physical ropes from physics-rope whose ends follow the plugs and
the player's hand, so the ropes swing and settle while the rules have already decided.
Features
- Crossings with an order — worked out from the ropes' paths, with the rope moved last on top.
- Three win conditions — untangled, every rope on its colour, or both.
- Rule options —
lockropes lying under others,clearfreed ropes from the board, and amoveslimit. - Any layout — grids, rings, hanging rows with one anchor per rope along the top, or sockets placed by hand.
- Generated levels — scrambled from a solved board, always solvable, with a solution and a par, from a seed.
- Hints — a best-first search for a move towards the win from wherever the player is.
- Undo, redo and saves —
undo(),redo(),restart(),snapshot()andrestore(). - Stars — from the level's par.
- Physical ropes for feel — ropes that follow the plugs and the hand, lying flat with no gravity or hanging with it, and fading out as a cleared rope leaves.
- A view to draw — sockets with their colour and plug, ropes bottom first with whether each is tangled, locked or home, and the crossings.
Good for
- Peg-board untangling puzzles in the style of Twisted Tangle, seen from above.
- Knot puzzles in the style of Tangle Master, where locked ropes make the order of moves matter.
- Hanging-rope colour sorts — ropes hanging from a bar that must reach the sockets of their colour.
- Endless or daily puzzles — seeded generation with a known solution and par for every level.
- Casual puzzle ladders — difficulty that rises with the minimum moves and the crossings.
Make it yours
generate takes a seed, the board's width and height (600 by 800), a layout, the number of ropes (5), at
least how many crossings, how many scramble moves, the minimum moves for the best solution, the rules, the
colours, and the search budget (4,000 states). A level written by hand sets its sockets, ropes, starting order,
rules and par. feel takes gravity, slack, radius and damping.
Two different uses: a peg board seen from above, generated on a four-by-five grid with six ropes and a minimum of four
moves, played with the untangled win and ropes that lie flat with no gravity; and a hanging-rope colour sort, with
ropes anchored along a bar, the targets win, lock and clear on, and the feel's gravity at about 900 so the ropes
hang.
A quick look
A generated level, a plug dragged and dropped, and the physical ropes drawn bottom first:
const level = game.ropeUntangle.generate({ seed: 4, layout: { kind: 'grid', cols: 4, rows: 5 }, ropes: 6, minimum: 4 });
const play = game.ropeUntangle.create(level);
const ropes = game.ropeUntangle.feel(play); // physical ropes, for feel
ropes.hold('r2', 1, pointer); // the plug follows the hand
const result = play.move({ rope: 'r2', end: 1, to: 's11' }); // { ok: true } or { ok: false, reason }
ropes.release();
game.step('untangle', dt => ropes.step(dt));
for (const r of ropes.view()) drawRope(r.points, r.colour, r.layer); // bottom first
Works with
physics-rope— the rope world behind the physical ropes.cut-rope— for rope puzzles about cutting rather than untangling.pull-pin,screw-sortandscrew-relocate— other casual physics puzzles.
Good to know
- For the rules, a rope's path is the straight line between its ends: ropes cross when those lines cross, not where the physical ropes happen to lie.
- The search is best first, not exhaustive, so
paris the shortest solution it found rather than necessarily the shortest there is, and a large board with locking can need more than the default budget for a hint. - The physical ropes do not collide with each other or the pegs, and lie in the rules' over-and-under order only as drawn.
- It is 2D in the board's own units and draws nothing; a 3D presenter can stand the same board up.






