UNPKG

@logic-pad/core

Version:
147 lines (146 loc) 5.17 kB
import { ConfigType } from '../config.js'; import GridData from '../grid.js'; import { array } from '../dataHelper.js'; import { Color, State } from '../primitives.js'; import CustomTextSymbol from '../symbols/customTextSymbol.js'; import Rule from './rule.js'; export default class MysteryRule extends Rule { solution; visible; title = 'Alternate Solution'; get configExplanation() { return 'You may use multiple instances of this rule to provide multiple alternate solutions.'; } static EXAMPLE_GRID = Object.freeze(GridData.create(['.']).addSymbol(new CustomTextSymbol('', GridData.create([]), 0, 0, '?'))); static CONFIGS = Object.freeze([ { type: ConfigType.Tile, default: MysteryRule.EXAMPLE_GRID, resizable: false, field: 'solution', description: 'Solution', explanation: 'The alternate solution to the puzzle. Does not need to satisfy puzzle rules / symbols.', configurable: true, }, { type: ConfigType.Boolean, default: true, field: 'visible', description: 'Visible', explanation: 'Display this rule to the player.', configurable: true, }, ]); static SEARCH_VARIANTS = [ new MysteryRule(MysteryRule.EXAMPLE_GRID, true).searchVariant(), ]; /** * **Mystery: alternate solution** */ constructor(solution, visible) { super(); this.solution = solution; this.visible = visible; this.solution = MysteryRule.cleanSolution(solution); this.visible = visible; } get id() { return `mystery`; } get explanation() { return `*Mystery:* Alternate solution`; } get visibleWhenSolving() { return this.visible; } get configs() { return MysteryRule.CONFIGS; } createExampleGrid() { return MysteryRule.EXAMPLE_GRID; } get searchVariants() { return MysteryRule.SEARCH_VARIANTS; } validateGrid(grid) { if (grid.colorEquals(this.solution)) return { state: State.Satisfied }; return { state: State.Incomplete }; } get necessaryForCompletion() { return false; } onFinalValidation(grid, _solution, state) { if (State.isSatisfied(state.final)) return state; if (grid.colorEquals(this.solution)) return { final: State.Satisfied, symbols: state.symbols, rules: state.rules, }; return state; } onGridChange(newGrid) { if (newGrid.width === this.solution.width && newGrid.height === this.solution.height) { if (!newGrid.tiles.some((row, y) => row.some((tile, x) => { const solutionTile = this.solution.getTile(x, y); if (solutionTile.exists !== tile.exists) return true; if (solutionTile.fixed !== tile.fixed) return true; if (solutionTile.exists && solutionTile.fixed && solutionTile.color !== tile.color) return true; return false; }))) return this; } return this.withSolution(MysteryRule.cleanSolution(this.solution, newGrid)); } onGridResize(_grid, mode, direction, index) { if (mode === 'insert') { if (direction === 'row') { return this.withSolution(this.solution.insertRow(index)); } else if (direction === 'column') { return this.withSolution(this.solution.insertColumn(index)); } } else if (mode === 'remove') { if (direction === 'row') { return this.withSolution(this.solution.removeRow(index)); } else if (direction === 'column') { return this.withSolution(this.solution.removeColumn(index)); } } return this; } copyWith({ solution, visible, }) { return new MysteryRule(solution ?? this.solution, visible ?? this.visible); } withSolution(solution) { return this.copyWith({ solution }); } withVisible(visible) { return this.copyWith({ visible }); } static cleanSolution(solution, baseGrid) { const tiles = baseGrid ? array(baseGrid.width, baseGrid.height, (x, y) => { const tile = baseGrid.getTile(x, y); if (!tile.exists || tile.fixed) return tile; const solutionTile = solution.getTile(x, y); if (!solutionTile.exists || solutionTile.color === Color.Gray) return tile; return tile.withColor(solutionTile.color); }) : solution.tiles; return GridData.create(baseGrid?.width ?? solution.width, baseGrid?.height ?? solution.height, tiles); } } export const instance = new MysteryRule(GridData.create([]), true);