UNPKG

@logic-pad/core

Version:
147 lines (146 loc) 5.16 kB
import { ConfigType } from '../config.js'; import GridData from '../grid.js'; import { array } from '../dataHelper.js'; import { Color, State } from '../primitives.js'; import { getShapeVariants, sanitizePatternGrid, tilesToShape, } from '../shapes.js'; import Rule from './rule.js'; class BanPatternRule extends Rule { /** * **Don't make this pattern** * * @param pattern - GridData representing the banned pattern. Only non-gray tiles are considered. */ constructor(pattern) { super(); Object.defineProperty(this, "pattern", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "cache", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.pattern = sanitizePatternGrid(pattern); this.cache = getShapeVariants(tilesToShape(this.pattern.tiles)); } get id() { return `ban_pattern`; } get explanation() { return `Don't make this pattern`; } get configs() { return BanPatternRule.CONFIGS; } createExampleGrid() { let minX = Number.POSITIVE_INFINITY; let minY = Number.POSITIVE_INFINITY; let maxX = Number.NEGATIVE_INFINITY; let maxY = Number.NEGATIVE_INFINITY; this.pattern.forEach((tile, x, y) => { if (tile.color !== Color.Gray && tile.exists) { minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y); } }); const width = maxX - minX + 1; const height = maxY - minY + 1; const tiles = array(width, height, (x, y) => { const tile = this.pattern.getTile(x + minX, y + minY); if (!tile.exists || tile.color !== Color.Gray) return tile; return tile.withExists(false); }); return GridData.create(width, height, tiles); } get searchVariants() { return BanPatternRule.SEARCH_VARIANTS; } validateGrid(grid) { for (const pattern of this.cache) { let startX, startY, endX, endY; if (grid.wrapAround.value) { startX = -pattern.width; startY = -pattern.height; endX = grid.width - 1; endY = grid.height - 1; } else { startX = 0; startY = 0; endX = grid.width - pattern.width; endY = grid.height - pattern.height; } for (let y = startY; y <= endY; y++) { for (let x = startX; x <= endX; x++) { let match = true; const visited = []; for (const tile of pattern.elements) { const pos = grid.toArrayCoordinates(x + tile.x, y + tile.y); if (grid.wrapAround.value && // optimization: no need to check visited if wrapAround is disabled visited.some(p => p.x === pos.x && p.y === pos.y)) { match = false; break; } visited.push(pos); const t = grid.getTile(x + tile.x, y + tile.y); if (!t.exists || t.color !== tile.color) { match = false; break; } } if (match) { return { state: State.Error, positions: pattern.elements.map(tile => grid.toArrayCoordinates(x + tile.x, y + tile.y)), }; } } } } return { state: grid.isComplete() ? State.Satisfied : State.Incomplete }; } copyWith({ pattern }) { return new BanPatternRule(pattern ?? this.pattern); } withPattern(pattern) { return this.copyWith({ pattern }); } } Object.defineProperty(BanPatternRule, "EXAMPLE_GRID", { enumerable: true, configurable: true, writable: true, value: Object.freeze(GridData.create(['nnnnn', 'nnnnn', 'wwwwn', 'nnnnn', 'nnnnn'])) }); Object.defineProperty(BanPatternRule, "CONFIGS", { enumerable: true, configurable: true, writable: true, value: Object.freeze([ { type: ConfigType.Tile, default: BanPatternRule.EXAMPLE_GRID, resizable: true, field: 'pattern', description: 'Pattern', configurable: true, }, ]) }); Object.defineProperty(BanPatternRule, "SEARCH_VARIANTS", { enumerable: true, configurable: true, writable: true, value: [ new BanPatternRule(BanPatternRule.EXAMPLE_GRID).searchVariant(), ] }); export default BanPatternRule; export const instance = new BanPatternRule(GridData.create([]));