cable-puzzle-engine
v1.0.1
Published
Pure-logic engine for cable routing puzzles: types, rotation, flood-fill connectivity, ASCII renderer, and interactive CLI.
Readme
cable-puzzle-engine
Pure TypeScript logic for a two-color cable routing puzzle: rotated tiles, per-color connectivity (flood fill), “solved” detection, level serialization, and terminal renderers. There is no UI framework here—only deterministic functions you can drive from a game client, tests, or a small REPL.
Typical uses:
- Game / app integration — given a board layout and endpoints, ask whether red and blue each form a continuous path from entry to exit (
isColorConnected,isBoardSolved). - Level design — author boards as JSON, YAML, or TOON; round-trip with
boardTo*/boardFrom*. - Debugging & docs — print boards as ASCII, Unicode box-drawing, or half-block “pixel” art (
boardToAscii,boardToBoxArt,boardToPixelArt). - CLI playground — interactive session to rotate cells, swap pieces, move endpoints, and export levels.
Puzzle model (short)
- Grid:
rows × cols. Coordinates in the API and in level files are 1-based:row1 is the top row,col1 is the left column. - Cells: each has a
pieceTypeandrotation(0,90,180, or270degrees clockwise). - Ports: each piece exposes red and/or blue connections on sides (
top,right,bottom,left). Rotation moves those sides around the cell. - Connectivity: two adjacent cells connect for a color if both expose that color on the touching sides (e.g. red on my
bottomand red on the neighbor’stop). - Endpoints: each color has an entry and exit
{ row, col, side }. The solver checks whether there is a single connected path for that color from entry cell to exit cell, with the path entering/leaving through the specified sides of those cells.
Design note (from the engine): in the product this was built for, cables are expected to enter from the bottom of the last row and exit from the top of the first row. The engine itself still accepts any
sideon the endpoints you configure; the CLI’s “empty board” presets follow that convention.
Piece types (ports at rotation 0°)
| pieceType | Red ports | Blue ports |
|----------------|------------|------------|
| red-straight | top, bottom | — |
| blue-straight | — | top, bottom |
| red-corner | top, right | — |
| blue-corner | — | top, right |
| red-T | top, bottom, left | — |
| blue-T | — | top, bottom, left |
| rb-corners | top, right | bottom, left |
| rb-cross | top, bottom | left, right |
Use getPiecePortsAtZero(pieceType) or getCellPorts(cell) in code to inspect ports after rotation.
Library API
Install (once published) or depend on the package path; then import from the package entry:
import {
makeBoard,
isColorConnected,
isBoardSolved,
getReachableCells,
getCellPorts,
boardToJSON,
boardFromJSON,
boardToYAML,
boardFromYAML,
boardToTOON,
boardFromTOON,
boardToAscii,
boardToBoxArt,
boardToPixelArt,
} from "cable-puzzle-engine";Board construction
makeBoard(cells, redEntry, redExit, blueEntry, blueExit)— builds aBoardConfigfrom a 2DCell[][]and four endpoints.
Connectivity
isColorConnected(board, "red" | "blue")— flood fill from that color’s entry to its exit.isBoardSolved(board)— both colors connected.getReachableCells(board, color)— set of"row,col"keys reachable from entry (for highlighting / analytics).
Geometry helpers
oppositeSide,adjacentCell,getCellSidesForColor,getPiecePortsAtZero.
Serialization (same logical schema for JSON / YAML; TOON uses a flat cells list with row, col for compact files)
boardToJSON/boardFromJSONboardToYAML/boardFromYAML(usesyaml)boardToTOON/boardFromTOON(uses@toon-format/toon)
Renderers (strings suitable for logs or terminal)
boardToAscii— plain text, no ANSI colors.boardToBoxArt— colored box-drawing; highlights reachable cells.boardToPixelArt— half-block dense view; highlights reachable cells.
See examples/ for sample .json / .toon levels.
Interactive CLI
From source (development)
cd packages/cable-puzzle-engine
npm install
npm run cli # same as: npx tsx src/cli.tsStartup flags
| Flag | Meaning |
|------|--------|
| (none) | Load built-in 3×2 demo (red column vs blue column; rotate toward 0° to practice). |
| --rows <n> --cols <m> | Empty board filled with red-straight at 0°, with default corner endpoints. |
| --level <path> | Load a level file; format by extension: .json → JSON, .yaml / .yml → YAML, .toon → TOON. (see /examples folder) |
Examples:
npx tsx src/cli.ts
npx tsx src/cli.ts --rows 7 --cols 5
npx tsx src/cli.ts --level examples/2x3.jsonREPL commands
After launch, type commands at the cable> prompt:
| Command | Description |
|--------|-------------|
| r <row> <col> / rotate | Rotate that cell 90° clockwise. |
| set <row> <col> <pieceType> [rotation] | Replace cell; rotation defaults to 0. |
| ep <color> entry-or-exit <row> <col> <side> | Set endpoint: color is red or blue, second token is entry or exit, side is top, right, bottom, or left. |
| resize <rows> <cols> | New empty board (same default endpoints pattern as --rows/--cols). |
| reset | Restore initial cell rotations/pieces from when the board was loaded. |
| board | Re-print the colored summary view. |
| solve | Same summary (shows per-color connected ✓/✗ and solved banner). |
| ascii | Plain ASCII grid. |
| box | Box-drawing + colors + reachability highlight. |
| px / pixel | Half-block pixel art + reachability highlight. |
| pieces | List all pieceType ids and port shapes at 0°. |
| export [json \| yaml \| toon] | Print level to stdout (default json). |
| import <file> | Load .json, .yaml, .yml, or .toon from disk. |
| help / ? | Command list. |
| q / quit / exit | Exit. |
Tests & build
npm test # Jest
npm run build # emits JavaScript + .d.ts to dist/Publishing as its own npm package (checklist)
When you copy this folder out of the monorepo into a new repository:
Repository layout — keep at least:
src/,package.json,tsconfig.json,jest.config.js,README.md,examples/,__tests__/, andbin/(see below).package.jsonadjustments- Name: pick an unused npm name (e.g.
@scope/cable-puzzle-engineorcable-puzzle-engine). - Entry points for consumers: point published code at built output, for example:
"main": "dist/index.js""types": "dist/index.d.ts"- Optionally add
"exports"if you want explicit subpath control.
files: includedist/,src/(only if you want to ship sources),bin/,README.md,examples/as needed. Do not publish secrets or local-only files.license: replaceUNLICENSEDwith a real SPDX license if the package is public.- Scripts: add something like
"prepare": "npm run build"sonpm publishalways runstscfirst (or useprepublishOnly). bin: this package declarescable-puzzle→bin/cli.mjs. Afternpm run build, that binary loadsdist/cli.js. Runnpm linklocally to smoke-test the global command.
- Name: pick an unused npm name (e.g.
Git
git init,.gitignore(node_modules,distif you prefer not committing build output, coverage, etc.), commit, create remote on GitHub/GitLab, push.
npm
- Log in:
npm login. - Dry run:
npm publish --dry-runand inspect the tarball contents. - First publish:
npm publish(use--access publicfor scoped public packages).
- Log in:
Consumers — they install the package and import from the published
main/types; they do not needtsxunless they run your CLI from TypeScript sources directly.
License
See package.json (license field).
