A small, dependency-free JavaScript solver for water sort / color sort / ball sort puzzles with 3, 4 or 5 layers per tube. It runs in the browser or in Node, finds a move list for a stuck level, proves when a shelf has no solution, and encodes levels as short share links.
Try it online, with a tube editor and step-by-step replay: chaoschemy.com/tools/water-sort-solver
It is the same solver used by Potion Sort, a free water sort puzzle with a daily shelf.
import { solveTubes, stepsText, encodeLevel } from './solver.mjs';
// Each tube lists its layers from the bottom up; colors are palette indexes (0 = red, 1 = orange, ...).
const tubes = [[0, 1, 0, 1], [1, 0, 1, 0], [], []];
const result = solveTubes(tubes, 4);
console.log(result.status); // 'solved'
console.log(stepsText(result, { capacity: 4, tubes }));
console.log(encodeLevel({ capacity: 4, tubes })); // c4-abab-baba-0-0solveTubes(tubes, capacity) returns { status } where status is one of:
| status | meaning |
|---|---|
solved |
path (pairs of tube indexes), moves (from, to, color, amount) and states (the shelf after each move) |
sorted |
already solved |
stuck |
no legal pour |
impossible |
an exhaustive search proved there is no solution |
invalid |
the shelf breaks the rules; problems says why (wrong layer counts, overfull tubes, unknown colors) |
unknown |
the search budget ran out |
Other exports: checkTubes, guidedSearch, exhaustiveSearch, applyPour, pourBlock, isSolved, hasPour, replayStates, randomShelf, encodeLevel, decodeLevel, PALETTE, CAPACITIES.
Pour rules follow the common mobile games: you pour the top run of one color onto the same color or into an empty tube, as much as fits. The solver first runs a weighted best-first search (the heuristic counts color breaks and unfinished tubes) with a node budget, retries wider and greedier, and finally runs an exhaustive search with state hashing so it can prove a level impossible instead of just giving up.
node examples/cli.mjs "c4-abab-baba-0-0"
node --testMIT