UNPKG

@logic-pad/core

Version:
184 lines (183 loc) 7.09 kB
import { Color, State } from '../../primitives.js'; import { instance as lyingSymbolInstance } from '../../rules/lyingSymbolRule.js'; import { instance as offByXInstance } from '../../rules/offByXRule.js'; import { instance as lotusInstance } from '../../symbols/lotusSymbol.js'; import { instance as galaxyInstance } from '../../symbols/galaxySymbol.js'; import { instance as wrapAroundInstance } from '../../rules/wrapAroundRule.js'; import { allSolvers } from '../allSolvers.js'; import Solver from '../solver.js'; import UndercluedRule from '../../rules/undercluedRule.js'; import validateGrid from '../../validate.js'; class AutoSolver extends Solver { constructor() { super(...arguments); Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: 'auto' }); Object.defineProperty(this, "author", { enumerable: true, configurable: true, writable: true, value: 'various contributors' }); Object.defineProperty(this, "description", { enumerable: true, configurable: true, writable: true, value: 'Automatically select the fastest solver based on supported instructions and environment.' }); Object.defineProperty(this, "supportsCancellation", { enumerable: true, configurable: true, writable: true, value: true }); } isGridSupported(grid) { for (const solver of allSolvers.values()) { if (solver.id === this.id) continue; if (solver.isGridSupported(grid)) { return true; } } return false; } isInstructionSupported(instructionId) { for (const solver of allSolvers.values()) { if (solver.id === this.id) continue; if (solver.isInstructionSupported(instructionId)) { return true; } } return false; } async isEnvironmentSupported() { for (const solver of allSolvers.values()) { if (solver.id === this.id) continue; if (await solver.environmentCheck.value) { return true; } } return false; } fillSolution(grid, solution) { return grid.withTiles(tiles => { return tiles.map((row, y) => row.map((tile, x) => { if (!tile.exists || tile.fixed) return tile; const solutionTile = solution.tiles[y][x]; return tile.withColor(solutionTile.color); })); }); } fixGrid(grid) { return grid.withTiles(tiles => { return tiles.map(row => row.map(tile => { if (tile.fixed) return tile; return tile.withFixed(tile.color !== Color.Gray); })); }); } async *solveWithProgress(solver, grid, progress, abortSignal) { for await (const updatedGrid of solver.solve(progress, abortSignal)) { if (abortSignal?.aborted) return; if (!updatedGrid) return updatedGrid; yield this.fillSolution(grid, updatedGrid); } } async solveOne(generator) { // eslint-disable-next-line no-unreachable-loop for await (const grid of generator) { return grid; } return null; } async *solve(grid, abortSignal) { if (!!grid.findRule(r => AutoSolver.nonAdditiveInstructions.has(r.id)) || !!grid.findSymbol(s => AutoSolver.nonAdditiveInstructions.has(s.id))) { for (const solver of allSolvers.values()) { if (solver.id === this.id) continue; if (solver.isGridSupported(grid)) { yield* solver.solve(grid, abortSignal); return; } } throw new Error('No solver supports the given grid'); } else { let progressGrid = grid; for (const solver of allSolvers.values()) { if (solver.id === this.id) continue; if (solver.isGridSupported(progressGrid)) { yield* this.solveWithProgress(solver, grid, progressGrid, abortSignal); return; } else if (solver.isGridSupported(grid)) { yield* solver.solve(grid, abortSignal); return; } else { let undercluedGrid = progressGrid .withRules(rules => rules.filter(r => solver.isInstructionSupported(r.id))) .withSymbols(symbols => { for (const id of symbols.keys()) { if (!solver.isInstructionSupported(id)) symbols.delete(id); } return symbols; }) .addRule(new UndercluedRule()); if (!solver.isGridSupported(undercluedGrid)) { // special case for solvers that support lotus and galaxy symbols but not dual-color placement undercluedGrid = undercluedGrid.withSymbols(symbols => { symbols.delete(lotusInstance.id); symbols.delete(galaxyInstance.id); return symbols; }); } if (!solver.isGridSupported(undercluedGrid)) continue; const undercluedSolution = await this.solveOne(this.solveWithProgress(solver, progressGrid, undercluedGrid, abortSignal)); if (undercluedSolution === null) continue; if (undercluedSolution.getTileCount(true, false, Color.Gray) === 0) { const result = this.fillSolution(grid, undercluedSolution); if (validateGrid(result, null).final !== State.Error) { yield result; yield null; return; } else { yield null; return; } } progressGrid = this.fixGrid(undercluedSolution); } } yield this.fillSolution(grid, progressGrid); } } } Object.defineProperty(AutoSolver, "nonAdditiveInstructions", { enumerable: true, configurable: true, writable: true, value: new Set([ offByXInstance.id, lyingSymbolInstance.id, wrapAroundInstance.id, ]) }); export default AutoSolver;