UNPKG

grilops

Version:

A Grid Logic Puzzle Solver library, using Typescript and z3.

1,577 lines 68 kB
var it = Object.defineProperty; var nt = (r, s, e) => s in r ? it(r, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[s] = e; var a = (r, s, e) => (nt(r, typeof s != "symbol" ? s + "" : s, e), e); import { Z3_error_code as rt } from "z3-solver"; function B(...r) { const s = Math.min(...r.map((e) => e.length)); return Array.from({ length: s }, (e, t) => r.map((i) => i[t])); } function* $(r, s) { if (s === 0) yield []; else for (let e = 0; e < r.length; e++) { const t = r[e]; for (const i of $(r.slice(e + 1), s - 1)) yield [t, ...i]; } } function Y(r) { return class extends r { constructor(t, i) { super(i); a(this, "default"); this.default = t; } get(t) { return this.has(t) || this.set(t, this.default()), super.get(t); } }; } function O(r, s) { return class extends Map { get(t) { return super.get(r(t)); } has(t) { return super.has(r(t)); } set(t, i) { return super.set(r(t), i); } delete(t) { return super.delete(r(t)); } forEach(t, i) { super.forEach( (n, o, h) => t(n, s(o), h), i ); } *[Symbol.iterator]() { for (const [t, i] of super[Symbol.iterator]()) yield [s(t), i]; } constructor(t) { super( [...t ?? []].map(([i, n]) => [r(i), n]) ); } }; } function k(r, s) { return class extends Set { has(t) { return super.has(r(t)); } add(t) { return super.add(r(t)); } delete(t) { return super.delete(r(t)); } forEach(t, i) { super.forEach( (n, o, h) => t( s(n), s(o), h ), i ); } *[Symbol.iterator]() { for (const t of super[Symbol.iterator]()) yield s(t); } constructor(t) { super([...t ?? []].map((i) => r(i))); } }; } class d { constructor(s, e) { /** * The relative distance in the y dimension. */ a(this, "dy"); /** * The relative distance in the x dimension. */ a(this, "dx"); this.dy = s, this.dx = e; } /** * Returns a vector that's the negation of this one. */ negate() { return new d(-this.dy, -this.dx); } /** * Translates this vector's endpoint in the given direction. */ translate(s) { return new d(this.dy + s.dy, this.dx + s.dx); } toString() { return `V(${this.dy},${this.dx})`; } static fromString(s) { const e = s.match(/^V\((\d+),(\d+)\)$/); if (e === null) throw new Error(`Invalid VectorString: ${s}`); return new d(parseInt(e[1]), parseInt(e[2])); } equals(s) { return this.dy === s.dy && this.dx === s.dx; } static comparator(s, e) { return s.dy < e.dy ? -1 : s.dy > e.dy ? 1 : s.dx < e.dx ? -1 : s.dx > e.dx ? 1 : 0; } } const U = O((r) => r.toString(), d.fromString), ot = k((r) => r.toString(), d.fromString), at = Y(U); class p { constructor(s, e) { /** * The name of the direction. */ a(this, "name"); /** * The vector of the direction. */ a(this, "vector"); this.name = s, this.vector = e; } toString() { return `D(${this.name},${this.vector.toString()})`; } static fromString(s) { const e = s.match(/^D\((\w+),(V\(\d+,\d+\))\)$/); if (e === null) throw new Error(`Invalid DirectionString: ${s}`); return new p( e[1], d.fromString(e[2]) ); } } const C = O( (r) => r.toString(), p.fromString ), dt = k( (r) => r.toString(), p.fromString ), H = Y(C); class m { constructor(s, e) { /** * The location in the y dimension. */ a(this, "y"); /** * The location in the x dimension. */ a(this, "x"); this.y = s, this.x = e; } /** * Translates this point by the given `Vector` or `Direction`. */ translate(s) { return s instanceof p && (s = s.vector), new m(this.y + s.dy, this.x + s.dx); } toString() { return `P(${this.y},${this.x})`; } static fromString(s) { const e = s.match(/^P\((\d+),(\d+)\)$/); if (e === null) throw new Error(`Invalid PointString: ${s}`); return new m(parseInt(e[1]), parseInt(e[2])); } equals(s) { return this.y === s.y && this.x === s.x; } static comparator(s, e) { return s.y < e.y ? -1 : s.y > e.y ? 1 : s.x < e.x ? -1 : s.x > e.x ? 1 : 0; } } const g = O((r) => r.toString(), m.fromString), Q = k((r) => r.toString(), m.fromString), Z = Y(g); class J { constructor(s, e, t) { /** * The location of the cell. */ a(this, "location"); /** * The direction from the original cell. */ a(this, "direction"); /** * The symbol constant of the cell. */ a(this, "symbol"); this.location = s, this.direction = e, this.symbol = t; } } class I { constructor() { a(this, "_vectorDirection"); this._vectorDirection = new U(); for (const s of this.vertexSharingDirections()) this._vectorDirection.set(s.vector, s); } /** * Given a direction, return the opposite direction. * @param direction The given `Direction`. * @returns The `Direction` opposite the given direction. */ oppositeDirection(s) { return this._vectorDirection.get(s.vector.negate()); } /** * Returns a list of points that share an edge with the given cell. * @param point The point of the given cell. * @returns A list of `Point`s in the lattice that correspond to cells that * share an edge with the given cell. */ edgeSharingPoints(s) { return this.edgeSharingDirections().map( (e) => s.translate(e) ); } /** * Returns a list of points that share a vertex with the given cell. * @param point The point of the given cell. * @returns A list of `Point`s in the lattice corresponding to cells that * share a vertex with the given cell. */ vertexSharingPoints(s) { return this.vertexSharingDirections().map( (e) => s.translate(e) ); } /** * Returns a list of neighbors in the given directions of the given cell. * @param cellMap A dictionary mapping points in the lattice to z3 constants. * @param p Point of the given cell. * @param directions The given list of directions to find neighbors with. * @returns A list of `Neighbor`s corresponding to the cells that are in the * given directions from the given cell. */ static _getNeighbors(s, e, t) { const i = []; for (const n of t) { const o = e.translate(n), h = s.get(o); h !== void 0 && i.push(new J(o, n, h)); } return i; } /** * Returns a list of neighbors sharing an edge with the given cell. * @param cellMap A dictionary mapping points in the lattice to z3 constants. * @param p Point of the given cell. * @returns A list of `Neighbor`s corresponding to the cells that share an * edge with the given cell. */ edgeSharingNeighbors(s, e) { return I._getNeighbors(s, e, this.edgeSharingDirections()); } /** * Returns a list of neighbors sharing a vertex with the given cell. * @param cellMap A dictionary mapping points in the lattice to z3 constants. * @param p Point of the given cell. * @returns A list of `Neighbor`s corresponding to the cells that share a * vertex with the given cell. */ vertexSharingNeighbors(s, e) { return I._getNeighbors(s, e, this.vertexSharingDirections()); } /** * Prints something for each of the given points. * @param hookFunction A function implementing per-location display * behavior. It will be called for each `Point` in the lattice. If the * returned string has embedded newlines, it will be treated as a multi-line * element. For best results, all elements should have the same number of * lines as each other and as blank (below). * @param ps The `Point`s to print something for. * @param blank What to print for `Point`s not in the lattice, or for when * the hook function returns None. Defaults to one space. If it has * embedded newlines, it will be treated as a multi-line element. */ _pointsToString(s, e, t = " ") { const i = []; for (const n of e) { let o; this.pointToIndex(n) !== void 0 && (o = s(n)), o = o ?? t, i.push(o.split(` `)); } return B(...i).map((n) => n.join("")).join(` `) + ` `; } /** * Prints something for each space in the lattice. * * Printing is done from top to bottom and left to right. * * @param hookFunction A function implementing per-location display * behavior. It will be called for each `Point` in the lattice. If the * returned string has embedded newlines, it will be treated as a multi-line * element. For best results, all elements should have the same number of * lines as each other and as blank (below). * @param blank What to print for `Point`s not in the lattice, or for when * the hook function returns None. Defaults to one space. If it has * embedded newlines, it will be treated as a multi-line element. */ toString(s, e = " ") { let t = ""; const i = this.points, n = i[0].y, o = i[i.length - 1].y, h = Math.min(...i.map((l) => l.x)), c = Math.max(...i.map((l) => l.x)); for (let l = n; l <= o; l++) t += this._pointsToString( s, Array.from( { length: c - h + 1 }, (x, _) => new m(l, _ + h) ), e ); return t; } } const y = class y extends I { /** * @param points A set of points corresponding to a rectangular lattice. * Note that these points need not fill a complete rectangle. */ constructor(e) { super(); a(this, "_points"); a(this, "_pointIndices"); this._points = e.sort(m.comparator), this._pointIndices = new g(), this._points.forEach((t, i) => { this._pointIndices.set(t, i); }); } get points() { return this._points; } pointToIndex(e) { return this._pointIndices.get(e); } edgeSharingDirections() { return Object.values(y.EDGE_DIRECTIONS); } vertexSharingDirections() { return Object.values(y.VERTEX_DIRECTIONS); } labelForDirection(e) { if (e.name === "N") return "╵"; if (e.name === "E") return "╶"; if (e.name === "S") return "╷"; if (e.name === "W") return "╴"; throw new Error("No single-character symbol for direction"); } labelForDirectionPair(e, t) { if (e.name === "N" && t.name === "S") return "│"; if (e.name === "E" && t.name === "W") return "─"; if (e.name === "N" && t.name === "E") return "└"; if (e.name === "S" && t.name === "E") return "┌"; if (e.name === "S" && t.name === "W") return "┐"; if (e.name === "N" && t.name === "W") return "┘"; throw new Error("No single-character symbol for direction pair"); } transformationFunctions(e, t) { return e ? t ? [ (i) => i, (i) => new d(i.dy, -i.dx), (i) => new d(-i.dy, i.dx), (i) => new d(-i.dy, -i.dx), (i) => new d(i.dx, i.dy), (i) => new d(i.dx, -i.dy), (i) => new d(-i.dx, i.dy), (i) => new d(-i.dx, -i.dy) ] : [ (i) => i, (i) => new d(i.dx, -i.dy), (i) => new d(-i.dy, -i.dx), (i) => new d(-i.dx, i.dy) ] : t ? [ (i) => i, (i) => new d(i.dy, -i.dx), (i) => new d(-i.dy, i.dx) ] : [(i) => i]; } getInsideOutsideCheckDirections() { const e = this.edgeSharingDirections(); return [e.find((t) => t.name === "N"), [e.find((t) => t.name === "W")]]; } }; a(y, "EDGE_DIRECTIONS", { N: new p("N", new d(-1, 0)), S: new p("S", new d(1, 0)), E: new p("E", new d(0, 1)), W: new p("W", new d(0, -1)) }), a(y, "VERTEX_DIRECTIONS", { ...y.EDGE_DIRECTIONS, NE: new p("NE", new d(-1, 1)), NW: new p("NW", new d(-1, -1)), SE: new p("SE", new d(1, 1)), SW: new p("SW", new d(1, -1)) }); let N = y; const D = class D extends I { /** * A set of points forming a hexagonal lattice. * * This abstract class implements functions identical between * FlatToppedHexagonalLattice and PointyToppedHexagonalLattice. * * We use the doubled coordinates scheme described at * https://www.redblobgames.com/grids/hexagons/. That is, y describes * the row and x describes the column, so x + y is always even. */ constructor(e) { super(); a(this, "_points"); a(this, "_pointIndices"); for (const t of e) if ((t.y + t.x) % 2 === 1) throw new Error("Hexagonal coordinates must have an even sum."); this._points = e.sort(m.comparator), this._pointIndices = new g(), this._points.forEach((t, i) => { this._pointIndices.set(t, i); }); } get points() { return this._points; } pointToIndex(e) { return this._pointIndices.get(e); } vertexSharingDirections() { return this.edgeSharingDirections(); } labelForDirection(e) { const t = D._DIRECTION_LABELS[e.name]; if (t !== void 0) return t; throw new Error("No single-character symbol for direction"); } labelForDirectionPair(e, t) { let i = [e.name, t.name]; function n(x, _) { for (const [u, et] of B(x, _)) if (i.includes(u)) return i = i.filter((st) => st !== u), et.toString(); return " "; } const o = n(["NW", "N", "W"], ["╲", "▕", "▁"]), h = n(["NE", "N", "E"], ["╱", "▏", "▁"]), c = n(["SW", "S", "W"], ["╱", "▕", "▔"]), l = n(["SE", "S", "E"], ["╲", "▏", "▔"]); return `${o}${h} ${c}${l}`; } }; a(D, "_DIRECTION_LABELS", { N: `▕ `, NE: ` ╱ `, E: ` ▁ `, SE: ` ╲`, S: ` ▕ `, SW: ` ╱ `, W: `▁ `, NW: `╲ ` }); let G = D; const P = class P extends G { edgeSharingDirections() { return Object.values(P.DIRECTIONS); } transformationFunctions(s, e) { return s ? e ? [ (t) => t, (t) => new d((t.dy + 3 * t.dx) / 2, (-t.dy + t.dx) / 2), (t) => new d((-t.dy + 3 * t.dx) / 2, (-t.dy - t.dx) / 2), (t) => new d(-t.dy, -t.dx), (t) => new d((-t.dy - 3 * t.dx) / 2, (t.dy - t.dx) / 2), (t) => new d((t.dy - 3 * t.dx) / 2, (t.dy + t.dx) / 2), (t) => new d(-t.dy, t.dx), (t) => new d((-t.dy - 3 * t.dx) / 2, (-t.dy + t.dx) / 2), (t) => new d((t.dy - 3 * t.dx) / 2, (-t.dy - t.dx) / 2), (t) => new d(t.dy, -t.dx), (t) => new d((t.dy + 3 * t.dx) / 2, (t.dy - t.dx) / 2), (t) => new d((-t.dy + 3 * t.dx) / 2, (t.dy + t.dx) / 2) ] : [ (t) => t, (t) => new d((t.dy + 3 * t.dx) / 2, (-t.dy + t.dx) / 2), (t) => new d((-t.dy + 3 * t.dx) / 2, (-t.dy - t.dx) / 2), (t) => new d(-t.dy, -t.dx), (t) => new d((-t.dy - 3 * t.dx) / 2, (t.dy - t.dx) / 2), (t) => new d((t.dy - 3 * t.dx) / 2, (t.dy + t.dx) / 2) ] : e ? [ (t) => t, (t) => new d(-t.dy, t.dx), (t) => new d((-t.dy - 3 * t.dx) / 2, (-t.dy + t.dx) / 2), (t) => new d((t.dy - 3 * t.dx) / 2, (-t.dy - t.dx) / 2), (t) => new d(t.dy, -t.dx), (t) => new d((t.dy + 3 * t.dx) / 2, (t.dy - t.dx) / 2), (t) => new d((-t.dy + 3 * t.dx) / 2, (t.dy + t.dx) / 2) ] : [(t) => t]; } getInsideOutsideCheckDirections() { const s = this.edgeSharingDirections(); return [ s.find((e) => e.name === "N"), [s.find((e) => e.name === "NW"), s.find((e) => e.name === "SW")] ]; } }; a(P, "DIRECTIONS", { N: new p("N", new d(-2, 0)), S: new p("S", new d(2, 0)), NE: new p("NE", new d(-1, 1)), NW: new p("NW", new d(-1, -1)), SE: new p("SE", new d(1, 1)), SW: new p("SW", new d(1, -1)) }); let F = P; const q = class q extends G { edgeSharingDirections() { return Object.values(q.DIRECTIONS); } transformationFunctions(s, e) { return s ? e ? [ (t) => t, (t) => new d((t.dy + t.dx) / 2, (-3 * t.dy + t.dx) / 2), (t) => new d((-t.dy + t.dx) / 2, (-3 * t.dy - t.dx) / 2), (t) => new d(-t.dy, -t.dx), (t) => new d((-t.dy - t.dx) / 2, (3 * t.dy - t.dx) / 2), (t) => new d((t.dy - t.dx) / 2, (3 * t.dy + t.dx) / 2), (t) => new d(-t.dy, t.dx), (t) => new d((-t.dy - t.dx) / 2, (-3 * t.dy + t.dx) / 2), (t) => new d((t.dy - t.dx) / 2, (-3 * t.dy - t.dx) / 2), (t) => new d(t.dy, -t.dx), (t) => new d((t.dy + t.dx) / 2, (3 * t.dy - t.dx) / 2), (t) => new d((-t.dy + t.dx) / 2, (3 * t.dy + t.dx) / 2) ] : [ (t) => t, (t) => new d((t.dy + t.dx) / 2, (-3 * t.dy + t.dx) / 2), (t) => new d((-t.dy + t.dx) / 2, (-3 * t.dy - t.dx) / 2), (t) => new d(-t.dy, -t.dx), (t) => new d((-t.dy - t.dx) / 2, (3 * t.dy - t.dx) / 2), (t) => new d((t.dy - t.dx) / 2, (3 * t.dy + t.dx) / 2) ] : e ? [ (t) => new d(-t.dy, t.dx), (t) => new d((-t.dy - t.dx) / 2, (-3 * t.dy + t.dx) / 2), (t) => new d((t.dy - t.dx) / 2, (-3 * t.dy - t.dx) / 2), (t) => new d(t.dy, -t.dx), (t) => new d((t.dy + t.dx) / 2, (3 * t.dy - t.dx) / 2), (t) => new d((-t.dy + t.dx) / 2, (3 * t.dy + t.dx) / 2) ] : [(t) => t]; } getInsideOutsideCheckDirections() { const s = this.edgeSharingDirections(); return [ s.find((e) => e.name === "E"), [s.find((e) => e.name === "NW"), s.find((e) => e.name === "NE")] ]; } }; a(q, "DIRECTIONS", { E: new p("E", new d(0, 2)), W: new p("W", new d(0, -2)), NE: new p("NE", new d(-1, 1)), NW: new p("NW", new d(-1, -1)), SE: new p("SE", new d(1, 1)), SW: new p("SW", new d(1, -1)) }); let W = q; function L(r, s) { const e = []; for (let t = 0; t < r; t++) for (let i = 0; i < s; i++) e.push(new m(t, i)); return new N(e); } function ht(r) { return L(r, r); } const ct = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, DefaultDirectionMap: H, DefaultPointMap: Z, DefaultVectorMap: at, Direction: p, DirectionMap: C, DirectionSet: dt, FlatToppedHexagonalLattice: F, HexagonalLattice: G, Lattice: I, Neighbor: J, Point: m, PointMap: g, PointSet: Q, PointyToppedHexagonalLattice: W, RectangularLattice: N, Vector: d, VectorMap: U, VectorSet: ot, getRectangleLattice: L, getSquareLattice: ht }, Symbol.toStringTag, { value: "Module" })), M = class M { /** * @param context The context in which to construct the grid. * @param lattice The structure of the grid. * @param symbolSet The set of symbols to be filled into the grid. * @param solver A `Solver` object. If undefined, a `Solver` will be constructed. */ constructor(s, e, t, i = void 0) { a(this, "ctx"); a(this, "_lattice"); a(this, "_symbolSet"); a(this, "_solver"); a(this, "_grid"); this.ctx = s, this._lattice = e, this._symbolSet = t, this._solver = i ?? new this.ctx.context.Solver(), this._grid = new g(); for (const n of e.points) { const o = this.ctx.context.Int.const( `sg_${M._instanceIndex}_${n.y}-${n.x}` ); this._solver.add(o.ge(t.minIndex()), o.le(t.maxIndex())), this._grid.set(n, o); } } /** * The `Solver` object associated with this `SymbolGrid`. */ get solver() { return this._solver; } /** * The `grilops.symbols.SymbolSet` associated with this `SymbolGrid`. */ get symbolSet() { return this._symbolSet; } /** * The grid of cells. */ get grid() { return this._grid; } /** * The lattice of points in the grid. */ get lattice() { return this._lattice; } /** * Returns a list of cells that share an edge with the given cell. * @param p The location of the given cell. * @returns A `Neighbor[]` representing the cells sharing * an edge with the given cell. */ edgeSharingNeighbors(s) { return this._lattice.edgeSharingNeighbors(this._grid, s); } /** * Returns the cells that share a vertex with the given cell. * * In other words, returns a list of cells orthogonally and diagonally * adjacent to the given cell. * @param p The location of the given cell. * @returns A `Neighbor[]` representing the cells sharing * a vertex with the given cell. */ vertexSharingNeighbors(s) { return this._lattice.vertexSharingNeighbors(this._grid, s); } /** * Returns the cell at the given point. * @param p The location of the cell. * @returns The cell at the given point. */ cellAt(s) { return this._grid.get(s); } /** * Returns an expression for whether this cell contains this value. * @param p The location of the given cell. * @param value The value to satisfy the expression. * @returns An expression that's true if and only if the cell at p contains * this value. */ cellIs(s, e) { return this._grid.get(s).eq(e); } /** * Returns an expression for whether this cell contains one of these values. * @param p The location of the given cell. * @param values The set of values to satisfy the expression. * @returns An expression that's true if and only if the cell at p contains * one of these values. */ cellIsOneOf(s, e) { const t = this._grid.get(s); return this.ctx.context.Or(...e.map((i) => t.eq(i))); } /** * Returns true if the puzzle has a solution, false otherwise. */ async solve() { return await this._solver.check() === "sat"; } /** * Returns true if the solution to the puzzle is unique, false otherwise. * * Should be called only after `SymbolGrid.solve` has already completed * successfully. */ async isUnique() { const s = this._solver.model(), e = []; for (const t of this._grid.values()) e.push(t.neq(s.eval(t))); return this._solver.add(this.ctx.context.Or(...e)), await this._solver.check() === "unsat"; } /** * Returns the solved symbol grid. * * Should be called only after `SymbolGrid.solve` has already completed * successfully. */ solvedGrid() { const s = this._solver.model(), e = new g(); for (const [t, i] of this._grid.entries()) e.set(t, Number(s.eval(i))); return e; } /** * Prints the solved grid using symbol labels. * * Should be called only after `SymbolGrid.solve` has already completed * successfully. * @param hookFunction A function implementing custom symbol display * behavior, or None. If this function is provided, it will be called for * each cell in the grid, with the arguments p (`Point`) * and the symbol index for that cell (`number`). It may return a string to * print for that cell, or None to keep the default behavior. */ toString(s) { const e = this._solver.model(), t = Math.max( ...[...this.symbolSet.symbols.values()].map((n) => n.label.length) ), i = (n) => { var l; const o = this._grid.get(n), h = Number(e.eval(o)); let c; return s && (c = s(n, h)), c === void 0 && (c = (l = this.symbolSet.symbols.get(h)) == null ? void 0 : l.label.padStart(t)), c; }; return this._lattice.toString(i, " ".repeat(t)); } }; a(M, "_instanceIndex", 0); let R = M; function lt(r) { return { SymbolGrid: function(s, e, t = void 0) { return new R(r, s, e, t); } }; } class z { constructor(s, e, t) { a(this, "ctx"); a(this, "_exprs", /* @__PURE__ */ new Map()); a(this, "_exprFuncs"); a(this, "_point"); a(this, "_yMin", Number.NaN); a(this, "_yMax", Number.NaN); a(this, "_xMin", Number.NaN); a(this, "_xMax", Number.NaN); a(this, "_yMid", Number.NaN); a(this, "_xMid", Number.NaN); a(this, "_tl"); a(this, "_tr"); a(this, "_bl"); a(this, "_br"); a(this, "_quads", []); if (this.ctx = s, e.length === 0) throw new Error( "A quadtree node must be constructed with at least one point" ); if (this._exprFuncs = t ?? /* @__PURE__ */ new Map(), this._point = e.length === 1 ? e[0] : void 0, !this._point) { this._yMin = Math.min(...e.map((n) => n.y)), this._yMax = Math.max(...e.map((n) => n.y)), this._xMin = Math.min(...e.map((n) => n.x)), this._xMax = Math.max(...e.map((n) => n.x)), this._yMid = (this._yMin + this._yMax) / 2, this._xMid = (this._xMin + this._xMax) / 2; const i = (n) => { const o = e.filter(n); if (o.length > 0) return new z(this.ctx, o, this._exprFuncs); }; this._tl = i((n) => n.y < this._yMid && n.x < this._xMid), this._tr = i((n) => n.y < this._yMid && n.x >= this._xMid), this._bl = i((n) => n.y >= this._yMid && n.x < this._xMid), this._br = i((n) => n.y >= this._yMid && n.x >= this._xMid), this._quads = [this._tl, this._tr, this._bl, this._br].filter( Boolean ); } } /** * Returns true if the given point is within this tree node's bounds. */ coversPoint(s) { return this._point ? this._point.equals(s) : s.y >= this._yMin && s.y <= this._yMax && s.x >= this._xMin && s.x <= this._xMax; } /** * Registers an expression constructor, to be called for each point. */ addExpr(s, e) { this._exprFuncs.set(s, e); } /** * Returns expressions for all points covered by this tree node. */ getExprs(s) { if (this._point) { let e = this._exprs.get(s); return e || (e = this._exprFuncs.get(s)(this._point), this._exprs.set(s, e)), [e]; } return this._quads.flatMap((e) => e.getExprs(s)); } /** * Returns the expression for the given point. */ getPointExpr(s, e) { if (this._point) { if (this._point.equals(e)) { let t = this._exprs.get(s); return t || (t = this._exprFuncs.get(s)(this._point), this._exprs.set(s, t)), t; } throw new Error(`Point ${e.toString()} not in QuadTree`); } if (this._tl && e.y < this._yMid && e.x < this._xMid) return this._tl.getPointExpr(s, e); if (this._tr && e.y < this._yMid && e.x >= this._xMid) return this._tr.getPointExpr(s, e); if (this._bl && e.y >= this._yMid && e.x < this._xMid) return this._bl.getPointExpr(s, e); if (this._br && e.y >= this._yMid && e.x >= this._xMid) return this._br.getPointExpr(s, e); throw new Error(`Point ${e.toString()} not in QuadTree`); } /** * Returns the conjunction of all expressions, excluding given points. */ getOtherPointsExpr(s, e) { if (this._point) return e.some((n) => this._point.equals(n)) ? void 0 : this.getPointExpr(s, this._point); const t = e.filter((n) => this.coversPoint(n)); if (t.length > 0) { const n = this._quads.map((o) => o.getOtherPointsExpr(s, t)).filter(Boolean); return this.ctx.context.And(...n); } let i = this._exprs.get(s); return i || (i = this.ctx.context.And(...this.getExprs(s)), this._exprs.set(s, i)), i; } } function _t(r) { return { ExpressionQuadTree: function(s, e) { return new z(r, s, e); } }; } let E = class { /** * @param index The index value assigned to the symbol. * @param name The code-safe name of the symbol. * @param label The printable label of the symbol. */ constructor(s, e, t) { a(this, "_index"); a(this, "_name"); a(this, "_label"); this._index = s, this._name = e, this._label = t; } /** * The index value assigned to the symbol. */ get index() { return this._index; } /** * The code-safe name of the symbol. */ get name() { return this._name ?? this._label ?? this._index.toString(); } /** * The printable label of the symbol. */ get label() { return this._label ?? this._name ?? this._index.toString(); } toString() { return this.label; } }; class A { /** * @param symbols A list of specifications for the symbols. Each specification * may be a code-safe name, a (code-safe name, printable label) tuple, or * a (code-safe name, printable label, index value) tuple. */ constructor(s) { a(this, "_indexToSymbol", /* @__PURE__ */ new Map()); a(this, "_labelToSymbolIndex", /* @__PURE__ */ new Map()); a(this, "indices", {}); var e; for (const t of s) if (typeof t == "string") { const i = this._nextUnusedIndex(); this._indexToSymbol.set(i, new E(i, t)); } else if (Array.isArray(t)) { let [i, n, o] = t; if (t.length === 3) { if (this._indexToSymbol.has(o)) throw new Error( `Index of ${t.toString()} already used by ${(e = this._indexToSymbol.get(o)) == null ? void 0 : e.toString()}` ); } else if (t.length === 2) o = this._nextUnusedIndex(); else throw new Error( `Invalid symbol spec: ${t.toString()}` ); this._indexToSymbol.set(o, new E(o, i, n)); } else throw new Error( `Invalid symbol spec: ${t.toString()}` ); for (const t of this._indexToSymbol.values()) this.indices[t.name] = t.index, this._labelToSymbolIndex.set(t.label, t.index); } _nextUnusedIndex() { return this._indexToSymbol.size === 0 ? 0 : Math.max(...this._indexToSymbol.keys()) + 1; } /** * Appends an additional symbol to this symbol set. * @param name The code-safe name of the symbol. * @param label The printable label of the symbol. */ append(s = void 0, e = void 0) { const t = this._nextUnusedIndex(), i = new E(t, s, e); this._indexToSymbol.set(t, i), this.indices[i.name] = i.index, this._labelToSymbolIndex.set(i.label, i.index); } /** * Returns the minimum index value of all of the symbols. */ minIndex() { return Math.min(...this._indexToSymbol.keys()); } /** * Returns the maximum index value of all of the symbols. */ maxIndex() { return Math.max(...this._indexToSymbol.keys()); } /** * The map of all symbols. */ get symbols() { return this._indexToSymbol; } toString() { return `SymbolSet(${[...this._indexToSymbol.values()].join(", ")})`; } } function pt(r, s) { const e = []; for (let t = r.charCodeAt(0); t <= s.charCodeAt(0); t++) e.push(String.fromCharCode(t)); return new A(e); } function xt(r, s) { const e = []; for (let t = r; t <= s; t++) e.push(["S" + t.toString(), t.toString(), t]); return new A(e); } const ut = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, Symbol: E, SymbolSet: A, makeLetterRangeSymbolSet: pt, makeNumberRangeSymbolSet: xt }, Symbol.toStringTag, { value: "Module" })); function K(r, s, e, t, i, n, o = () => r.context.Bool.val(!1)) { const h = [], c = [i]; let l = e; for (; s.grid.has(l); ) { const _ = s.grid.get(l), u = n(c[c.length - 1], _, l); c.push(u), h.push(o(u, _, l)), l = l.translate(t.vector); } let x = c.pop(); for (const [_, u] of B( h.reverse(), c.reverse() )) x = r.context.If(_, u, x); return x; } function gt(r, s, e, t, i = (o) => r.context.Int.val(1), n = (o) => r.context.Bool.val(!1)) { return K( r, s, e, t, r.context.Int.val(0), (o, h) => o.add(i(h)), (o, h) => n(h) ); } function mt(r) { return { /** * Returns a computation of a sightline through a grid. * @param context The context in which to construct the constraints. * @param symbolGrid The grid to check against. * @param start The location of the cell where the sightline should begin. * This is the first cell checked. * @param direction The direction to advance to reach the next cell in the * sightline. * @param initializer The initial value for the accumulator. * @param accumulate A function that accepts an accumulated value, a symbol, * and (optionally) a point as arguments, and returns a new accumulated * value. This function is used to determine a new accumulated value for * each cell along the sightline, based on the accumulated value from the * previously encountered cells as well as the point and/or symbol of the * current cell. * @param stop A function that accepts an accumulated value, a symbol, and * (optionally) a point as arguments, and returns True if we should stop * following the sightline when this symbol or point is encountered. By * default, the sightline will continue to the edge of the grid. * @returns The accumulated value. */ reduceCells: (s, e, t, i, n, o = () => r.context.Bool.val(!1)) => K( r, s, e, t, i, n, o ), /** * Returns a count of cells along a sightline through a grid. * @param context The context in which to construct the constraints. * @param symbolGrid The grid to check against. * @param start The location of the cell where the sightline should begin. * This is the first cell checked. * @param direction The direction to advance to reach the next cell in the * sightline. * @param count A function that accepts a symbol as an argument and returns the * integer value to add to the count when this symbol is encountered. By * default, each symbol will count with a value of one. * @param stop A function that accepts a symbol as an argument and returns True * if we should stop following the sightline when this symbol is * encountered. By default, the sightline will continue to the edge of the * grid. * @returns An `Arith` for the count of cells along the sightline through the * grid. */ countCells: (s, e, t, i = (o) => r.context.Int.val(1), n = (o) => r.context.Bool.val(!1)) => gt(r, s, e, t, i, n) }; } function tt(r) { if (r.z3.get_error_code(r.context.ptr) !== rt.Z3_OK) throw new Error( r.z3.get_error_msg( r.context.ptr, r.z3.get_error_code(r.context.ptr) ) ); } function ft(r, s) { return tt(r), s; } function yt(r, s, e) { s.__typename === "Solver" ? r.z3.solver_assert(r.context.ptr, s.ptr, e) : r.z3.optimize_assert(r.context.ptr, s.ptr, e), tt(r); } function St(r, s, e) { const t = s.map(([n, o]) => n.ast), i = s.map(([n, o]) => o); return ft( r, r.z3.mk_pbeq(r.context.ptr, t, i, e) ); } var bt = /* @__PURE__ */ ((r) => (r[r.HAS_INSTANCE_ID = 0] = "HAS_INSTANCE_ID", r[r.NOT_HAS_INSTANCE_ID = 1] = "NOT_HAS_INSTANCE_ID", r[r.HAS_SHAPE_TYPE = 2] = "HAS_SHAPE_TYPE", r))(bt || {}); class v { /** * @param offsets A list of offsets that define the shape. An offset may be a * `Vector`; or, to optionally associate a payload value with the offset, it * may be a `[Vector, Payload]`. A payload may be any z3 expression. */ constructor(s, e) { a(this, "ctx"); a(this, "_offsetTuples", []); this.ctx = s; for (const t of e) if (t instanceof d) this._offsetTuples.push([t, void 0]); else if (Array.isArray(t)) { const [i, n] = t; this._offsetTuples.push([i, n]); } else throw new Error(`Invalid shape offset: ${t}`); } /** * The offset vectors that define this shape. */ get offsetVectors() { return this._offsetTuples.map(([s]) => s); } /** * The offset vector and payload value tuples for this shape. */ get offsetsWithPayloads() { return this._offsetTuples; } /** * Returns a new shape with each offset transformed by `f`. */ transform(s) { return new v( this.ctx, this._offsetTuples.map(([e, t]) => [s(e), t]) ); } /** * Returns a new shape that's canonicalized. * * A canonicalized shape is in sorted order and its first offset is * `Vector`(0, 0). This helps with deduplication, since equivalent shapes * will be canonicalized identically. * * @returns A `Shape` of offsets defining the canonicalized version of the * shape, i.e., in sorted order and with first offset equal to * `Vector`(0, 0). */ canonicalize() { const s = this._offsetTuples.slice().sort(([t], [i]) => d.comparator(t, i)), e = s[0][0].negate(); return new v( this.ctx, s.map(([t, i]) => [ t.translate(e), i ]) ); } /** * Returns true iff the given shape is equivalent to this shape. */ equivalent(s) { if (this._offsetTuples.length !== s._offsetTuples.length) return !1; for (let e = 0; e < this._offsetTuples.length; e++) { const [t, i] = this._offsetTuples[e], [n, o] = s._offsetTuples[e]; if (!t.equals(n)) return !1; if (this.ctx.context.isExpr(i) && this.ctx.context.isExpr(o)) { if (!this.ctx.context.Eq(i, o)) return !1; } else if (i === void 0) { if (o !== void 0) return !1; } else if (o === void 0) { if (i !== void 0) return !1; } else if (i !== o) return !1; } return !0; } } const S = class S { /** * @param lattice The structure of the grid. * @param shapes A list of region shape definitions. The same region shape * definition may be included multiple times to indicate the number of times * that shape may appear (if allowCopies is false). * @param solver A `Solver` object. If undefined, a `Solver` will be constructed. * @param complete If true, every cell must be part of a shape region. * Defaults to false. * @param allowRotations If true, allow rotations of the shapes to be placed * in the grid. Defaults to false. * @param allowReflections If true, allow reflections of the shapes to be * placed in the grid. Defaults to false. * @param allowCopies If true, allow any number of copies of the shapes to * be placed in the grid. Defaults to false. */ constructor(s, e, t, i = void 0, n = !1, o = !1, h = !1, c = !1) { a(this, "ctx"); a(this, "_solver"); a(this, "_lattice"); a(this, "_complete"); a(this, "_allowCopies"); a(this, "_shapes"); a(this, "_variants", []); a(this, "_shapeTypeGrid"); a(this, "_shapeInstanceGrid"); a(this, "_shapePayloadGrid"); this.ctx = s, S._instanceIndex += 1, this._solver = i ?? new this.ctx.context.Solver(), this._lattice = e, this._complete = n, this._allowCopies = c, this._shapes = t, this._makeVariants(o, h), this._createGrids(), this._addConstraints(); } _makeVariants(s, e) { const t = this._lattice.transformationFunctions( s, e ); this._variants = []; for (const i of this._shapes) { const n = []; for (const o of t) { const h = i.transform(o).canonicalize(); n.some((c) => c.equivalent(h)) || n.push(h); } this._variants.push(n); } } /** * Create the grids used to model shape region constraints. */ _createGrids() { this._shapeTypeGrid = /* @__PURE__ */ new Map(); for (const e of this._lattice.points) { const t = this.ctx.context.Int.const( `scst-${S._instanceIndex}-${e.y}-${e.x}` ); this._complete ? this._solver.add(t.ge(0)) : this._solver.add(t.ge(-1)), this._solver.add(t.lt(this._shapes.length)), this._shapeTypeGrid.set(e, t); } this._shapeInstanceGrid = /* @__PURE__ */ new Map(); for (const e of this._lattice.points) { const t = this.ctx.context.Int.const( `scsi-${S._instanceIndex}-${e.y}-${e.x}` ); this._complete ? this._solver.add(t.ge(0)) : this._solver.add(t.ge(-1)), this._solver.add(t.lt(this._lattice.points.length)), this._shapeInstanceGrid.set(e, t); } const s = this._shapes[0].offsetsWithPayloads[0][1]; if (s) { this._shapePayloadGrid = /* @__PURE__ */ new Map(); let e; if (this.ctx.context.isExpr(s)) e = s.sort; else if (typeof s == "number") e = this.ctx.context.Int.sort(); else throw new Error( `Could not determine z3 sort for ${s}` ); for (const t of this._lattice.points) { const i = this.ctx.context.Const( `scsp-${S._instanceIndex}-${t.y}-${t.x}`, e ); this._shapePayloadGrid.set(t, i); } } } _addConstraints() { if (this._addGridAgreementConstraints(), this._addShapeInstanceConstraints(), !this._allowCopies) for (let s = 0; s < this._shapes.length; s++) this._addSingleCopyConstraints(s, this._shapes[s]); } _addGridAgreementConstraints() { for (const [s, e] of this._shapeTypeGrid) this._solver.add( this.ctx.context.Or( this.ctx.context.And( e.eq(-1), this._shapeInstanceGrid.get(s).eq(-1) ), this.ctx.context.And( e.neq(-1), this._shapeInstanceGrid.get(s).neq(-1) ) ) ); } _addShapeInstanceConstraints() { const s = {}; for (let i = 0; i < Math.max(this._lattice.points.length, this._variants.length); i++) s[i] = this.ctx.context.Int.val(i); const e = new z(this.ctx, this._lattice.points); for (const i of this._lattice.points.map( (n) => this._lattice.pointToIndex(n) )) e.addExpr( `0-${i}`, (n) => this.ctx.context.Eq( this._shapeInstanceGrid.get(n), s[i] ) ), e.addExpr( `1-${i}`, (n) => this.ctx.context.Not( this.ctx.context.Eq( this._shapeInstanceGrid.get(n), s[i] ) ) ); for (let i = 0; i < this._variants.length; i++) e.addExpr( `2-${i}`, (n) => this.ctx.context.Eq(this._shapeTypeGrid.get(n), s[i]) ); const t = new Z(() => []); for (let i = 0; i < this._variants.length; i++) for (const n of this._variants[i]) for (const o of this._lattice.points) { const h = this._lattice.pointToIndex(o), c = []; for (const [l, x] of n.offsetsWithPayloads) { const _ = o.translate(l); if (!this._shapeInstanceGrid.has(_)) { c.length = 0; break; } c.push([_, x]); } if (c.length > 0) { const l = []; for (const [_, u] of c) l.push( e.getPointExpr( `0-${h}`, _ ) ), l.push( e.getPointExpr( `2-${i}`, _ ) ), this._shapePayloadGrid && l.push(this._shapePayloadGrid.get(_).eq(u)); const x = e.getOtherPointsExpr( `1-${h}`, c.map(([_]) => _) ); x && l.push(x), t.get(o).push(this.ctx.context.And(...l)); } } for (const i of this._lattice.points) { const n = this._lattice.pointToIndex(i), o = e.getOtherPointsExpr( `1-${n}`, [] ), h = t.get(i); h.length > 0 ? (h.push(o), this._solver.add(this.ctx.context.Or(...h))) : this._solver.add(o); } } _addSingleCopyConstraints(s, e) { const t = []; for (const i of this._shapeTypeGrid.values()) t.push([i.eq(s), 1]); yt( this.ctx, this._solver, St(this.ctx, t, e.offsetsWithPayloads.length) ); } /** * The `Solver` associated with this `ShapeConstrainer`. */ get solver() { return this._solver; } /** * A dictionary of z3 constants of shape types. * * Each cell contains the index of the shape type placed in that cell (as * indexed by the shapes list passed in to the `ShapeConstrainer` * constructor), or -1 if no shape is placed within that cell. */ get shapeTypeGrid() { return this._shapeTypeGrid; } getShapeTypeAt(s) { return this._shapeTypeGrid.get(s); } /** * z3 constants of shape instance IDs. * * Each cell contains a number shared among all cells containing the same * instance of the shape, or -1 if no shape is placed within that cell. */ get shapeInstanceGrid() { return this._shapeInstanceGrid; } getShapeInstanceAt(s) { return this._shapeInstanceGrid.get(s); } /** * z3 constants of the shape offset payloads initially provided. * * undefined if no payloads were provided during construction. */ get shapePayloadGrid() { return this._shapePayloadGrid; } getShapePayloadAt(s) { return this._shapePayloadGrid.get(s); } /** * Prints the shape type assigned to each cell. * * Should be called only after the solver has been checked. */ shapeTypesToString() { const s = this._solver.model(), e = [...this._shapeTypeGrid.keys()], t = Math.min(...e.map((c) => c.y)), i = Math.min(...e.map((c) => c.x)), n = Math.max(...e.map((c) => c.y)), o = Math.max(...e.map((c) => c.x)); let h = ""; for (let c = t; c <= n; c++) { for (let l = i; l <= o; l++) { const x = new m(c, l); let _ = -1; if (this._shapeTypeGrid.has(x)) { const u = this._shapeTypeGrid.get(x); _ = Number(s.eval(u)); } _ >= 0 ? h += _.toString().padStart(3, " ") : h += " "; } h += ` `; } return h; } /** * Prints the shape instance ID assigned to each cell. * * Should be called only after the solver has been checked. */ shapeInstancesToString() { const s = this._solver.model(), e = [...this._shapeInstanceGrid.keys()], t = Math.min(...e.map((c) => c.y)), i = Math.min(...e.map((c) => c.x)), n = Math.max(...e.map((c) => c.y)), o = Math.max(...e.map((c) => c.x)); let h = ""; for (let c = t; c <= n; c++) { for (let l = i; l <= o; l++) { const x = new m(c, l); let _ = -1; if (this._shapeInstanceGrid.has(x)) { const u = this._shapeInstanceGrid.get(x); _ = Number(s.eval(u)); } _ >= 0 ? h += _.toString().padStart(3, " ") : h += " "; } h += ` `; } return h; } }; a(S, "_instanceIndex", 0); let V = S; function wt(r) { return { Shape: function(s) { return new v(r, s); }, ShapeConstrainer: function(s, e, t = void 0, i = !1, n = !1, o = !1, h = !1) { return new V( r, s, e, t, i, n, o, h ); } }; } const b = 0, T = 1, f = class f { /** * @param lattice The structure of the grid. * @param solver A `Solver` object. If None, a `Solver` will be constructed. * @param complete If true, every cell must be part of a region. Defaults to * true. * @param rectangular If true, every region must be "rectangular"; for each * cell in a region, ensure that pairs of its neighbors that are part of * the same region each share an additional neighbor that's part of the * same region when possible. * @param minRegionSize The minimum possible size of a region. * @param maxRegionSize The maximum possible size of a region. */ constructor(s, e, t = void 0, i = !0, n = !1, o = void 0, h = void 0) { a(this, "ctx"); a(this, "_solver"); a(this, "_lattice"); a(this, "_complete"); a(this, "_minRegionSize"); a(this, "_maxRegionSize"); a(this, "_edgeSharingDirectionToIndex"); a(this, "_parentTypeToIndex"); a(this, "_parentTypes"); a(this, "_parentGrid"); a(this, "_subtreeSizeGrid"); a(this, "_regionIdGrid"); a(this, "_regionSizeGrid"); this.ctx = s, f._instanceIndex += 1, this._lattice = e, this._solver = t ?? new this.ctx.context.Solver(), this._complete = i, o !== void 0 ? this._minRegionSize = o : this._minRegionSize = 1, h !== void 0 ? this._maxRegionSize = h : this._maxRegionSize = this._lattice.points.length, this._manageEdgeSharingDirections(), this._createGrids(), this._addConstraints(), n && this._addRectangularConstraints(); } /** * Creates the structures used for managing edge-sharing directions. * * Creates the mapping between edge-sharing directions and the parent * indices corresponding to them. */ _manageEdgeSharingDirections() { this._edgeSharingDirectionToIndex = new C(), this._parentTypeToIndex = /* @__PURE__ */ new Map([ ["X", b], ["R", T] ]), this._parentTypes = ["X", "R"]; for (const s of this._lattice.edgeSharingDirections()) { const e = this._parentTypes.length; this._parentTypeToIndex.set(s.name, e), this._edgeSharingDirectionToIndex.set(s, e), this._parentTypes.push(s.name); } } /** * Create the grids used to model region constraints. */ _createGrids() { this._parentGrid = new g(); for (const s of this._lattice.points) { const e = this.ctx.context.Int.const( `rcp-${f._instanceIndex}-${s.y}-${s.x}` ); this._complete ? this._solver.add(e.ge(T)) : this._solver.add(e.ge(b)), this._solver.add(e.lt(this._parentTypes.length)), this._parentGrid.set(s, e); } this._subtreeSizeGrid = new g(); for (const s of this._lattice.points) { const e = this.ctx.context.Int.const( `rcss-${f._instanceIndex}-${s.y}-${s.x}` ); this._complete ? this._solver.add(e.ge(1)) : this._solver.add(e.ge(0)), this._solver.add(e.le(this._maxRegionSize)), this._subtreeSizeGrid.set(s, e); } this._regionIdGrid = new g(); for (const s of this._lattice.points) { const e = this.ctx.context.Int.const( `rcid-${f._instanceIndex}-${s.y}-${s.x}` ); this._complete ? this._solver.add(e.ge(0)) : this._solver.add(e.ge(-1)), this._solver.add(e.lt(this._lattice.points.length)); const t = this._parentGrid.get(s); this._solver.add(t.eq(b).implies(e.eq(-1))); const i = this._lattice.pointToIndex(s); if (i === void 0) throw new Error("Point index is undefined"); this._solver.add(t.eq(T).implies(e.eq(i))), this._regionIdGrid.set(s, e); } this._regionSizeGrid = new g(); for (const s of this._lattice.points) { const e = this.ctx.context.Int.const( `rcrs-${f._instanceIndex}-${s.y}-${s.x}` ); this._complete ? this._solver.add(e.ge(this._minRegionSize)) : this._solver.add(e.ge(this._minRegionSize).or(e.eq(-1))), this._solver.add(e.le(this._maxRegionSize)); const t = this._parentGrid.get(