UNPKG

@logic-pad/core

Version:
183 lines (182 loc) 7.57 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 Symbol from './symbol.js'; export default class MyopiaSymbol extends Symbol { diagonals; directions; get title() { return this.diagonals ? 'Framed Myopia Arrow' : 'Myopia Arrow'; } static CONFIGS = 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', explanation: 'Whether to check diagonals as well. Must be enabled for diagonal arrows.', configurable: true, }, { type: ConfigType.OrientationToggle, default: orientationToggle(Orientation.Up, Orientation.Right), field: 'directions', description: 'Directions', explanation: 'The directions in which an arrow is pointing. A dot will be displayed if no arrows are present.', configurable: true, }, ]); static EXAMPLE_GRID = 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)), ])); static EXAMPLE_DIAGONAL_GRID = 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)), ])); /** * **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); this.diagonals = diagonals; this.directions = 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; } descriptionEquals(other) { return this.id === other.id && this.explanation === other.explanation; } 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 }); } } export const instance = new MyopiaSymbol(0, 0, false, orientationToggle(Orientation.Up, Orientation.Right));