Reference
Rules, solvers and generation
import { … } from "@johnmorrisdotca/tsunagi";
22.4 kB minified and gzipped, with everything it imports (measured with esbuild)
Tsunagi: join each pair of marbles with a line, every line its own, filling
the board. The layouts and answers as codes, the rules a line must keep,
a solver that counts answers, a generator, the twists (walls, bridges,
waypoints, hexagons), the difficulty measure, and the level ladder. The
levels themselves are data, imported a size at a time
(@johnmorrisdotca/tsunagi/levels-7) or all through
@johnmorrisdotca/tsunagi/levels.
Exports
| Name | Kind | What it is |
|---|---|---|
allJoined | function | Solved: every pair joined, every open cell on a line, and every bridge gone over both ways. |
answerOf | function | The answer the lines make, in the answer's spelling (encodeAnswer). |
blockOf | function | The block a level is in, from 1. |
blockRange | function | A block's first and last level, the last no further than the size has. |
blocksIn | function | How many blocks a size of count levels has. |
bridgeAndWallCandidate | function | A board with bridges and the walls it needs besides: both twists at once, for the blocks after both are taught. |
bridgeCandidate | function | A board with want bridges, or null where this filling offers no place for them or the board has more than one answer. |
bridgesOf | function | The bridges of a board. |
candidate | function | One candidate level, or null: a filling, its layout, and the solver's proof that the layout has exactly one answer — the filling's own. |
CAPITAL_PAIRS | constant | How many of PAIR_LETTERS are not small letters: a board that uses all of them has small letters for stones. |
CELL_BLOCKED | constant | The decoded blocked-cell sentinel. |
CELL_BRIDGE | constant | The decoded bridge-cell sentinel. |
CELL_EMPTY | constant | A cell in a decoded layout: an empty cell, a blocked one, or a stone of pair n (0 for A). |
Challenge | type | A challenge a board can have. |
CHALLENGES | constant | WHAT A TSUNAGI LEVEL ASKS OF A PLAYER, read from its board: the challenges on it, where it sits in its block's lesson, and how hard it measured. |
challengesOf | function | The challenges on a board, in the order the ladder teaches them. |
cheatGame | function | Cheat: one unfinished line drawn as the answer has it, anything in its way cut back. |
cheatLine | function | CHEAT: one unfinished line drawn for the player, correctly. |
checkGame | function | Check: the pairs not joined yet are flagged, and how many cells are still empty is said. |
checkTsunagiAnswer | function | Validate that an answer fills this board with legal, separate lines connecting every pair. |
compareEdges | function | Edges in the one order a layout writes them: by their first cell, then their second. |
countSolutions | function | Count valid complete answers up to a limit within a bounded search budget. |
countSolutionsOfLevel | function | How a level is proved and measured: countSolutions for a board the search of this file has always counted (4×4 to 15×15, no portals), and for a bigger one, or one with portals, the SAT search started from the level's own answer (countSolutionsSat, binary pairs). |
countSolutionsSat | function | Counts a layout's answers by SAT, up to limit. |
dailyTsunagiLevel | function | The level of the day at a size: a whole number from 1 to that size's count of levels, or null for a size there are no levels of. |
decodeLayout | function | A layout read from its code, or null for a string that is not one: the wrong length, a stray character, a letter used other than twice, or letters not named in reading order. |
decodeLines | function | Lines read back from a progress code against the layout they were drawn on, or null for a code that does not describe lines on it: a start that is not a stone, a step from nowhere, a line through another pair's stone, a cell two lines claim. |
DIFFICULTY_WEIGHTS | constant | The weights of the four measures in the score. |
difficultyScores | function | Each level's score, 0 (easiest) to 100 (hardest), as the blend of its four percentiles among measures — one size's levels. |
dragFinger | function | A finger over finger, drawing the line of pair, with the reach it had: the lines after it and the reach now. |
dragGame | function | The finger carried into a cell: the line grows, shortens or cuts another back, as the rules say. |
dragThrough | function | A drag that jumped several cells between two pointer events (a quick flick): walked one cell at a time along the row, then the column, so a fast finger draws what a slow one would. |
dragTo | function | A drag of the pair being drawn into cell: one step, as the finger enters a cell. |
edgeKey | function | An edge between two neighbouring cells, the same whichever way round they are named. |
edgeModel | function | A layout written down as clauses (see the top of this file), or null where it cannot be solved at all: a stone its pair does not name, or a cell with too few ways in and out to hold a line. |
EdgeModel | type | The clauses a layout comes to, ready to be searched. |
edgeOpen | function | Whether a line may step from cell a to its neighbour b: no wall between them. |
encodeAnswer | function | A finished grid's code: the letter of the line through each cell, # where blocked, + on a bridge. |
encodeLayout | function | A layout's code, from its cells, walls, waypoints and whether it wraps: the inverse of decodeLayout. |
encodeLines | function | Encode current lines as native resumable progress for this layout. |
encodePortals | function | The portals as a layout writes them, after wrap: each pair as its smaller cell and its larger, the pairs in order; nothing where there are none. |
encodeWalls | function | The walls as a layout writes them, after the cells: nothing where there are none. |
Explosion | type | EXPLOSIONS: every so many strokes, a drawn line is broken. |
explosionAfter | function | What the stroke-th stroke sets off, from the lines as it left them; null when it sets nothing off: a board without explosions, a stroke between them, or no line drawn to break. |
explosionsAsChosen | function | A level's explosions as the player chose to play them at set-up: as made, SOFTENED — a boom in place of a blast, and half as often — or OFF. |
filled | function | How many open cells have a line through them or a stone on them, and how many there are: a bridge counts twice, once each way over it. |
FillingStyle | type | How a filling is laid, where it need not be the way it always was: how often a line takes its tightest way on, and whether it may start beside the tightest cell as well as on it. |
firstUnsolvedTsunagiLevel | function | The lowest level not yet solved, or null when every level of the size is. |
forcedShare | function | The share of a layout's empty cells filled by forced moves alone: each line's two ends are grown while one of them has exactly one empty neighbour to go to, until a pair's ends meet or no end has only one way on. |
gameOver | function | Whether the game takes no more strokes: solved, or out of strokes. |
helpOf | function | The help a game has used so far, the one that costs more where it used two; null for none. |
helpOpensNext | function | Whether a solve with this help (or none) opens what a solve opens: every help but explosions off, whose block's lesson was not played. |
hexCandidate | function | A HEXAGON: the board as Hexversi's honeycomb, the square's corners off the board, filled with lines that may step along either slant as well as the four ways a square allows (hexNeighboursOf). |
hexNeighboursOf | function | The six neighbours of a cell on the hexagon lattice, as indexes; fewer at an edge of the square (the hexagon's own edge is its # cells). |
hexNeighbourTable | function | Every cell's six neighbours on the hexagon lattice, worked out once for a size. |
hexRadius | function | The hexagon's radius on a board of this side: R, for a side of 2R + 1. |
inHex | function | Whether a cell of the square is inside the hexagon: at most R lattice steps from the middle. |
isTsunagiDay | function | Whether a text is a real date written YYYY-MM-DD: 2026-02-30 is not. |
isTsunagiLevel | function | Whether a number is a level this size has. |
isTwist | function | Whether a board is a twist: any challenge on it. |
joined | function | Whether a pair's line runs from one of its stones to the other. |
layoutCells | function | A layout's cells without its walls or wrap: the part of the code one character a cell. |
layoutNeighbours | function | Each cell's neighbours a line may step to on this board: the four around it, less any across a wall. |
layoutOf | function | The layout and answer the lines leave: their ends as stones, lettered in reading order, with whatever else the board has — blocked cells, bridges (in two lines' paths, drawn +) and walls. |
layoutStep | function | A layout's step from a to its neighbour b (stepBetween on its own kind of board), or 0. |
letGo | function | Letting go: a line of only its stone is no line (a tap on a stone clears it), and a line ends before a bridge it stopped on. |
levelCountOf | function | How many levels a size has in a set; zero for a size the set has none at. |
LevelMeasure | type | What is measured of one level. |
LevelRow | type | One level: its layout and its one answer, each a code (code.ts). |
levelSeed | function | The number a record keeps a level by: its own for the first set, TSUNAGI_PORTAL_SEED and its number for the portals. |
liftGame | function | The finger lifted: the stroke is counted if it changed the board, the board may be solved, and an explosion may go off. |
Lines | type | THE LINES A PLAYER HAS DRAWN, and what a press and a drag do to them. |
linesCodeFits | function | Whether a progress code has the shape of one: a size's cells in the progress alphabet. |
linesOfAnswer | function | A level's answer drawn back as lines: each pair's cells, in order, from its first marble to its second. |
LINK_BLOCKED | constant | The blocked-cell marker in a layout code. |
LINK_BRIDGE | constant | The bridge-cell marker in a layout code. |
LINK_EMPTY | constant | TSUNAGI, WRITTEN DOWN: a level's layout and a finished grid, as the strings every puzzle travels as (puzzleCode.ts is the spelling of the number grids; this is the spelling of this one). |
LINK_HEX | constant | The segment after the cells that makes a board a hexagon of hexagons. |
LINK_PORTALS | constant | The word after the cells that begins a list of portals, each as its two cells. |
LINK_SPARSE | constant | The segment after the cells that says a board has few, long lines. |
LINK_WALLS | constant | Where the walls start, after the cells. |
LINK_WRAP | constant | The segment after the cells that makes a board wrap. |
LinkCandidate | type | One candidate: its layout and answer codes, and what the solver said about it. |
LinkExtras | type | What a board has besides its lines: the cells no line enters, the bridges two lines cross, and the walls between cells. |
LinkLayout | type | A decoded board with pairs, cells, walls, waypoints, wrapping, portals and optional limits. |
measureLevel | function | Every measure of one level, from its layout and answer; null for a layout that does not read as one of this size. |
measureSolved | function | The same measures from a solve already made — how many positions and branches the solver took — so a caller that has just proved a level (the level tests) does not solve it a second time to measure it. |
MOST_PAIRS | constant | The most lines a board can have: as many as there are characters to name them. |
neighboursOf | function | The four neighbours of a cell on a square grid, as indexes; fewer at an edge. |
neighbourTable | function | Every cell's neighbours, worked out once for a size. |
newTsunagiGame | function | A game on a layout code, or null for a code that is no layout at that size. |
nextTsunagiLevel | function | The level to open on: the first open one not yet solved, or the last open one when every open level is solved. |
NO_REACH | constant | No distance: the finger draws toward the cell it is over. |
noLines | function | The initial lines rooted at each pair’s first endpoint. |
openTsunagiLevels | function | The levels that are open, given the ones solved: the first block of sixteen always, and each block after it once every level of the block before is solved. |
orderByDifficulty | function | The order to play a size's levels in: easiest first, by score, and among equal scores by their order in levels so the ranking is the same every time it is made. |
outOfStrokes | function | Whether the game is out of strokes before it is solved: it takes nothing more until restartGame. |
overBridge | function | The pair going over a bridge each way, or -1: across (left to right) and down. |
ownersOf | function | The pair whose line (or stone) holds each cell, CELL_EMPTY for none, CELL_BLOCKED where blocked, CELL_BRIDGE on a bridge (two lines may be there). |
PAIR_LETTERS | constant | The characters a pair may be named by, in order: the capital letters, the digits, the small letters, then twenty punctuation marks, eighty-two in all, enough for a 30×30 board with as many lines to the cell as a 15×15 has. |
portalExit | function | The cell a line comes out at after stepping from from into portal cell into: beyond the other portal, the same way on; -1 where there is none (off the board, or across a wall). |
portalsAreAmbiguous | function | Whether two ways for a line to go from one cell to another exist on a board with portals (a portal and a step beside it, or two portals, or one in two directions), which an answer's cells could not tell apart. |
pressAt | function | A press: the lines after it, and the pair now being drawn, or null where the press draws nothing. |
pressGame | function | A finger down on a cell: starts a line from a marble, or carries on from a line's cell. |
Random | type | A number in [0, 1), like Math.random, from a stream a seed fixes. |
randomFilling | function | Grid lines that fill every cell, as lists of cells; null when this attempt painted itself into a corner. |
Reach | type | HOW FAR A FINGER IS FROM THE END OF THE LINE IT DRAWS, in rows and columns, once that line has been through a portal. |
reducedCandidate | function | One board made by taking clues away, or null. |
ReducedCandidate | type | What a made board has besides its lines. |
ReduceOptions | type | Budgets and geometry choices for generating a puzzle by reducing clues. |
relettered | function | A code's letters renamed in reading order of first appearance: the one spelling of a layout or answer. |
renumberedRecord | function | A RECORD KEPT BY LEVEL NUMBER, MOVED TO THE NUMBERS THE LEVELS HAVE NOW. |
repairedCandidate | function | One candidate level made by MENDING rather than by luck, for boards too big for luck (12×12): a filling whose layout has a second answer is mended where the two answers differ — the line through a cell they disagree on is cut in two there, which adds a pair of stones the second answer cannot honour — and solved again, until it has exactly one answer, its own. |
restartGame | function | Restart: every line cleared and the strokes counted again (Undo can bring the lines back). |
SAT_BINARY_FROM_SIZE | constant | From which side the SAT solver writes a cell's pair as a binary number rather than a variable for each pair, which a board of dozens of pairs needs: every level to 15×15 was proved the first way and keeps its counts. |
SAT_FROM_SIZE | constant | From which side of the board a layout is counted by solveSat.ts rather than by the search below. |
SatColours | type | How a cell's pair is written as variables: a variable for each pair (the way every level to 15×15 was proved), or a binary number, which a board of many pairs can afford. |
seededRandom | function | A stream of numbers in [0, 1) fixed by a seed. |
setOfSeed | function | The set and the number in it a record's number names. |
shuffled | function | A copy of the list in a random order (Fisher–Yates); the list given is left alone. |
SolveCount | type | THE TSUNAGI SOLVER: how many ways a layout can be joined, up to a limit, and how much trying it took. |
sparseCandidate | function | One sparse candidate level, or null. |
sparseMost | function | The most lines a sparse board of this side has: two thirds of the side — six on a 9×9, where nine is usual. |
Step | type | WHERE A LINE MAY GO NEXT, on one board: the rules of a single step, which the solver, the check, the drawing and the answer's lines all read, so a bridge or a wall means the same thing everywhere. |
stepBetween | function | The way from a to its neighbour b as a step (-1, +1, -size, +size), counting a step across a wrapped edge as the step it is — right off the right edge is +1, though the cells' numbers differ by size - 1. |
stepTable | function | Every cell's steps on a board, worked out once. |
stoneLetters | function | The characters that name pairs in a layout's cells: the first thirty-six (capitals and digits), or all eighty-two where the board uses every one of the thirty-six, which is when its small letters are stones and not waypoints. |
strokesToExplosion | function | Strokes left before the next explosion: 1 means the next stroke sets one off. |
strongestTsunagiHelp | function | Of several helps, the one a solve is kept with: explosions off, then Cheat, then explosions softened. |
symmetryKey | function | The least spelling of a layout over its eight turns and mirrors: equal keys are the same board. |
tailWord | function | Which of the tail's words a segment is, and what it says; null for none of them (the walls list). |
transformed | function | A layout code turned or mirrored: turn quarter turns, then a mirror across the vertical when mirror. |
TSUNAGI_BLOCK | constant | TSUNAGI'S BLOCKS: its levels come sixteen at a time. |
TSUNAGI_DAILY_STRIDE | constant | Each day moves this many levels along a size's ladder, round to the start when it runs out. |
TSUNAGI_LEVEL_COUNTS | constant | How many levels each size has, read without loading the size. |
TSUNAGI_PORTAL_COUNTS | constant | The number of portal levels available at each supported size. |
TSUNAGI_PORTAL_SEED | constant | A portal level's number in a record that keeps a level by one number: this and its number in its own set, past every level of the first. |
TSUNAGI_PORTAL_SIZES | constant | A second set of levels, boards with portals (code.ts): a line goes in at one ring and out of the other. |
TSUNAGI_SIZES | constant | TSUNAGI'S LEVELS, COUNTED: which sizes there are, how many levels each has, and which of them a player has opened. |
tsunagiBand | function | Which third of a size a level sits in: its first third easy, its middle medium, its last hard. |
TsunagiBand | type | The third of a size a level sits in. |
TsunagiCheck | type | What a check says: joined, or the first reason it is not. |
tsunagiDay | function | The day a moment falls on, in UTC, or the same day if it is already written as one. |
TsunagiDay | type | A calendar date, YYYY-MM-DD. |
TsunagiExplosionChoice | type | How a board's explosions are played: as made, softened (a boom for a blast, half as often) or not at all. |
TsunagiGame | type | The complete immutable puzzle state, current stroke, undo history and assistance flags. |
TsunagiGameOptions | type | What a game is told when it starts. |
TsunagiHelp | type | The help a solve used: Cheat drew a line, or explosions were softened or turned off. |
tsunagiMarks | function | A level's measured difficulty, 1 (easiest) to 5, or null for a level the marks do not have. |
tsunagiProgress | function | Read the native completion, strokes, explosions and help status of a game. |
TsunagiProgress | type | What a game stands at: for a status line under the board. |
tsunagiRole | function | A level's part in its block's lesson, from the data the level script wrote (marks.data.ts), without its size's boards; null for none. |
TsunagiSet | type | Which set of levels: the ones from 4×4 up, or the ones with portals. |
turnsIn | function | How many times an answer's lines turn a corner, over all of them: a line going straight over a bridge turns nowhere on it. |
TwistCandidate | type | MAKING TSUNAGI'S TWISTS, on a desk: boards with bridges, and boards with walls and blocked cells (John, 2026-09-26, both chosen from the brainstorm; bridges first). |
twistRole | function | A level's part in its block's lesson, or null for a level that has none (1 to 14, or a 15th or 16th left plain because somebody had played it). |
TwistRole | type | Where a level sits in its block's lesson: its 15th teaches the block's twist, its 16th tests it. |
undoGame | function | Undo: the lines as they were before the last stroke. |
unjoinedPairs | function | The pairs not joined yet, for Check: every pair whose line does not run from one of its marbles to the other — a line stopped beside its partner looks joined and is not. |
VERSION | constant | The package's version. |
wallCandidate | function | A board with blockedWanted blocked cells and as many walls as it needs (at most mostWalls) to have one answer, or null. |
WAYPOINT_LETTERS | constant | The waypoints a layout may carry: a pair's capital in lower case, so only the first twenty-six pairs can have one. |
waypointCandidate | function | WAYPOINTS. |
withoutPortalTail | function | A line with the portal cells taken off its end, if it ends on any: a line is never left inside a portal. |
withPortals | function | A filling with portals put in: each portal is made by joining two lines into one that goes into a ring and out of another, and the two cells it takes are the portal. |
wrapCandidate | function | WRAP. |
wrappedStep | function | The cell beside at one step by (-1, +1, -size, +size) on a board that wraps: off one edge and in at the other. |