grilops
Version:
A Grid Logic Puzzle Solver library, using Typescript and z3.
1,577 lines • 68 kB
JavaScript
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(