UNPKG

@logic-pad/core

Version:
199 lines (198 loc) 7.76 kB
import { ConfigType } from '../config.js'; import { move } from '../dataHelper.js'; import GridData from '../grid.js'; import { Color, ORIENTATIONS, Orientation, State, orientationToggle, } from '../primitives.js'; import MultiEntrySymbol from './multiEntrySymbol.js'; class MyopiaSymbol extends MultiEntrySymbol { /** * **Viewpoint Numbers count visible cells in the four directions** * @param x - The x-coordinate of the symbol. * @param y - The y-coordinate of the symbol. * @param diagonals - Whether the symbol should consider diagonal directions. * @param directions - The directions in which an arrow is pointing. */ constructor(x, y, diagonals, directions) { super(x, y); Object.defineProperty(this, "diagonals", { enumerable: true, configurable: true, writable: true, value: diagonals }); Object.defineProperty(this, "directions", { enumerable: true, configurable: true, writable: true, value: directions }); this.directions = directions; this.diagonals = directions[Orientation.DownLeft] || directions[Orientation.DownRight] || directions[Orientation.UpLeft] || directions[Orientation.UpRight] ? true : diagonals; } get id() { return `myopia`; } get placementStep() { return 1; } get explanation() { return this.diagonals ? '*Framed myopia arrows* point to *all* the closest opposite cells in 8 directions' : '*Myopia arrows* point to *all* the closest cells of the opposite color'; } get configs() { return MyopiaSymbol.CONFIGS; } createExampleGrid() { return this.diagonals ? MyopiaSymbol.EXAMPLE_DIAGONAL_GRID : MyopiaSymbol.EXAMPLE_GRID; } validateSymbol(grid) { const tile = grid.getTile(this.x, this.y); if (!tile.exists || tile.color === Color.Gray) return State.Incomplete; const allDirections = this.diagonals ? ORIENTATIONS : [Orientation.Up, Orientation.Down, Orientation.Left, Orientation.Right]; const map = { up: { min: 0, max: 0, complete: true }, down: { min: 0, max: 0, complete: true }, left: { min: 0, max: 0, complete: true }, right: { min: 0, max: 0, complete: true }, 'up-left': { min: 0, max: 0, complete: true }, 'up-right': { min: 0, max: 0, complete: true }, 'down-left': { min: 0, max: 0, complete: true }, 'down-right': { min: 0, max: 0, complete: true }, }; const pos = { x: this.x, y: this.y }; allDirections.forEach(direction => { let stopped = false; grid.iterateDirectionAll(move(pos, direction), direction, t => { if (!t.exists) return true; if (t.color === tile.color) return true; stopped = true; return false; }, () => { map[direction].min++; }); if (!stopped && map[direction].min === 0) map[direction].min = Number.MAX_SAFE_INTEGER; stopped = false; grid.iterateDirectionAll(move(pos, direction), direction, t => { if (!t.exists) return true; if (t.color === tile.color || t.color === Color.Gray) return true; stopped = true; return false; }, t => { map[direction].max++; if (t.exists && t.color === Color.Gray) map[direction].complete = false; }); if (!stopped) map[direction].max = Number.MAX_SAFE_INTEGER; }); const pointedDirections = allDirections.filter(d => this.directions[d]); const otherDirections = allDirections.filter(d => !this.directions[d]); for (let i = 0; i < pointedDirections.length; i++) { const direction1 = pointedDirections[i]; for (let j = i + 1; j < pointedDirections.length; j++) { const direction2 = pointedDirections[j]; if (map[direction1].min > map[direction2].max) return State.Error; if (map[direction2].min > map[direction1].max) return State.Error; } } if (Math.min(...otherDirections.map(d => map[d].max)) <= Math.max(...pointedDirections.map(d => map[d].min))) return State.Error; if (pointedDirections.length === 0 && otherDirections.some(d => map[d].max !== Number.MAX_SAFE_INTEGER)) return State.Error; if (pointedDirections.some(d => map[d].complete && map[d].max === Number.MAX_SAFE_INTEGER)) return State.Error; if (pointedDirections.length > 0 && otherDirections.length > 0 && pointedDirections.every(d => map[d].complete) && Math.max(...pointedDirections.map(d => map[d].max)) < Math.min(...otherDirections.map(d => map[d].min))) return State.Satisfied; if (allDirections.every(d => map[d].complete)) return State.Satisfied; return State.Incomplete; } copyWith({ x, y, diagonals, directions, }) { return new MyopiaSymbol(x ?? this.x, y ?? this.y, diagonals ?? this.diagonals, directions ?? this.directions); } withDirections(directions) { return this.copyWith({ directions }); } withDiagonals(diagonals) { return this.copyWith({ diagonals }); } } Object.defineProperty(MyopiaSymbol, "CONFIGS", { enumerable: true, configurable: true, writable: true, value: Object.freeze([ { type: ConfigType.Number, default: 0, field: 'x', description: 'X', configurable: false, }, { type: ConfigType.Number, default: 0, field: 'y', description: 'Y', configurable: false, }, { type: ConfigType.Boolean, default: false, field: 'diagonals', description: 'Include diagonal directions', configurable: true, }, { type: ConfigType.OrientationToggle, default: orientationToggle(Orientation.Up, Orientation.Right), field: 'directions', description: 'Directions', configurable: true, }, ]) }); Object.defineProperty(MyopiaSymbol, "EXAMPLE_GRID", { enumerable: true, configurable: true, writable: true, value: Object.freeze(GridData.create(['wbwww', 'bwwwb', 'wwwww', 'wbwww']).withSymbols([ new MyopiaSymbol(1, 1, false, orientationToggle(Orientation.Up, Orientation.Left)), new MyopiaSymbol(4, 3, false, orientationToggle(Orientation.Up)), ])) }); Object.defineProperty(MyopiaSymbol, "EXAMPLE_DIAGONAL_GRID", { enumerable: true, configurable: true, writable: true, value: Object.freeze(GridData.create(['wbwww', 'bwwwb', 'wwwww', 'wbwww']).withSymbols([ new MyopiaSymbol(1, 2, true, orientationToggle(Orientation.UpLeft, Orientation.Down)), new MyopiaSymbol(3, 2, true, orientationToggle(Orientation.UpRight)), ])) }); export default MyopiaSymbol; export const instance = new MyopiaSymbol(0, 0, false, orientationToggle(Orientation.Up, Orientation.Right));