UNPKG

@logic-pad/core

Version:
147 lines (146 loc) 5.41 kB
import { Point, PointSet, SymbolGrid, SymbolSet, getRectangleLattice, } from 'grilops'; import Solver from '../solver.js'; import { allZ3Modules } from './modules/index.js'; import { Color } from '../../primitives.js'; import { init } from 'z3-solver'; import Z3SolverContext from './z3SolverContext.js'; import { array } from '../../dataHelper.js'; export default class Z3Solver extends Solver { constructor() { super(...arguments); Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: 'z3' }); Object.defineProperty(this, "author", { enumerable: true, configurable: true, writable: true, value: 'Lysine' }); Object.defineProperty(this, "description", { enumerable: true, configurable: true, writable: true, value: '(Obsolete) A WebAssembly solver that supports a limited set of rules and symbols.' }); Object.defineProperty(this, "supportsCancellation", { enumerable: true, configurable: true, writable: true, value: false }); } async isEnvironmentSupported() { try { await init(); return true; } catch (ex) { return false; } } async *solve(grid) { console.log('Initializing dependencies'); const { Z3, Context } = await init(); const z3Ctx = Context('main'); const grilopsCtx = { z3: Z3, context: z3Ctx, }; const symbolSet = new SymbolSet([ ['empty', ' '], [Color.Dark, 'B'], [Color.Light, 'W'], ]); const lattice = getRectangleLattice(grid.height, grid.width); const symbolGrid = new SymbolGrid(grilopsCtx, lattice, symbolSet, new z3Ctx.Solver('QF_FD')); const ctx = new Z3SolverContext(symbolGrid); console.log('Encoding constraints'); grid.forEach((tile, x, y) => { // encode all empty tiles if (!tile.exists) ctx.solver.add(symbolGrid.cellAt(new Point(y, x)).eq(symbolSet.indices.empty)); // encode all given tiles else if (tile.fixed) ctx.solver.add(symbolGrid.cellAt(new Point(y, x)).eq(symbolSet.indices[tile.color])); // make sure tiles are filled else { ctx.solver.add(symbolGrid.cellAt(new Point(y, x)).neq(symbolSet.indices.empty)); } }); // encode connections const visited = array(grid.width, grid.height, () => false); const queue = new PointSet(); grid.connections.edges.forEach(edge => { queue.add(new Point(edge.y1, edge.x1)); queue.add(new Point(edge.y2, edge.x2)); }); queue.forEach(point => { if (visited[point.y][point.x]) return; visited[point.y][point.x] = true; const connected = grid.connections.getConnectedTiles({ x: point.x, y: point.y, }); connected.forEach(p => (visited[p.y][p.x] = true)); const filtered = connected .filter(p => grid.getTile(p.x, p.y).exists) .map(p => new Point(p.y, p.x)); if (filtered.length < 2) return; for (let i = 1; i < filtered.length; i++) { ctx.solver.add(symbolGrid.cellAt(point).eq(symbolGrid.cellAt(filtered[i]))); } }); [...new Set(grid.rules.map(r => r.id))].forEach(ruleId => { if (!allZ3Modules.has(ruleId)) return; allZ3Modules.get(ruleId).encode(grid, ctx); }); [...grid.symbols.keys()].forEach(symbolId => allZ3Modules.get(symbolId).encode(grid, ctx)); const decodeResult = (model) => { const tiles = array(grid.width, grid.height, (x, y) => { const tile = grid.getTile(x, y); if (!tile.exists || tile.fixed) return tile; const color = Number(model.eval(symbolGrid.cellAt(new Point(y, x)))); return tile.withColor(symbolSet.symbols.get(color).name); }); return grid.withTiles(tiles); }; console.log('Solving'); console.time('Solve time'); const result = await symbolGrid.solve(); console.timeEnd('Solve time'); if (!result) { yield null; return; } const model = ctx.solver.model(); yield decodeResult(model); console.log('Checking uniqueness'); console.time('Uniqueness time'); const result2 = await symbolGrid.isUnique(); console.timeEnd('Uniqueness time'); if (result2) { yield null; return; } const model2 = ctx.solver.model(); yield decodeResult(model2); } isInstructionSupported(instructionId) { return allZ3Modules.has(instructionId); } isGridSupported(grid) { if (!super.isGridSupported(grid)) return false; if (grid.getTileCount(true, true, Color.Gray) > 0) return false; return true; } }