Skip to the content

Tsunagi繋ぎ

Join each pair with a separate line and fill the board, through fixed levels or seeded puzzles.

Tsunagi means joining in Japanese. About the name

Contents

Reference · Rules, solvers and generation

SolveCounttype

THE TSUNAGI SOLVER: how many ways a layout can be joined, up to a limit, and how much trying it took.

Run where the levels are made (scripts/tsunagi-levels.ts) and in the unit test that proves every shipped level has exactly one answer — never in a browser and never on a request. A level is a line of data by the time anybody plays it.

The rules it counts under are the published ones, and no more: a line runs cell to cell across and down, never through a wall (steps.ts), lines never cross or share a cell except on a bridge — where one goes straight across and a different one straight down — a waypoint is taken by its own pair's line and no other, on a board that wraps a line may leave one edge and come back at the other, and every open cell and both ways over every bridge are used. A line MAY run beside itself; the solver counts those answers too, so "exactly one" is a claim about every answer the rules allow, not only the tidy ones.

Search: each pair's line grows from its first stone towards its second. At every step the pair with the fewest ways on is grown (a pair with one is forced), after four checks that throw a hopeless grid away early:

  • every empty cell still has two ways in and out (an empty cell or an unfinished line's end beside it), or no line could pass through it;
  • every unfinished line's growing end and its far stone each have a way on;
  • the growing end and the far stone touch one common region of empty cells (or each other), or the line can never close;
  • every region of empty cells is touched at both ends by some unfinished line, or nothing could ever fill it.

Import it

import type { SolveCount } from "@johnmorrisdotca/tsunagi";

Definition

type SolveCount = {
    count: number;
    nodes: number;
    branches: number;
    solution: number[] | null;
    solutions: number[][];
    gaveUp: boolean;
};

Members

NameTypeDescription
countnumber

Answers found, never more than the limit asked for.

nodesnumber

Positions looked at: the measure of how much trying the layout takes.

branchesnumber

Positions where more than one way on had to be tried: the measure of guessing.

solutionnumber[] | null

The first answer found, as the pair through each cell; null for none.

solutionsnumber[][]

Every answer found, in the order found: a second one is where a generator mends a board that has two (repairedCandidate).

gaveUpboolean

True when the search stopped at the node budget before it could say.

Examples

import { TSUNAGI_4 } from "@johnmorrisdotca/tsunagi/levels-4";
import { countSolutions, decodeLayout } from "@johnmorrisdotca/tsunagi";
import type { SolveCount } from "@johnmorrisdotca/tsunagi";

const givens = TSUNAGI_4[0][0];
const layout = decodeLayout(givens,4)!;
const value: SolveCount = countSolutions(layout,2,20000);
console.log(value);
{
  count: 1,
  nodes: 13,
  branches: 0,
  solution: [
    0, 0, 0, 0, 1, 1,
    1, 1, 2, 2, 2, 2,
    3, 3, 3, 3
  ],
  solutions: [
    [
      0, 0, 0, 0, 1, 1,
      1, 1, 2, 2, 2, 2,
      3, 3, 3, 3
    ]
  ],
  gaveUp: false
}

What it prints (run when this page was built)

View sourceEdit on GitHubMade from the source of @johnmorrisdotca/tsunagi 1.7.0.