@logic-pad/core
Version:
249 lines (248 loc) • 10 kB
JavaScript
import { ConfigType } from '../config.js';
import GridData from '../grid.js';
import { Color, Direction, State } from '../primitives.js';
import Symbol from './symbol.js';
function getColor(c, grid) {
if (!grid.isPositionValid(c.x, c.y) || !grid.getTile(c.x, c.y).exists) {
return null;
}
return grid.getTile(c.x, c.y).color;
}
function makeTurtle(pos1, pos2, grid) {
return {
pos1,
color1: getColor(pos1, grid),
pos2,
color2: getColor(pos2, grid),
};
}
class DirectionLinkerSymbol extends Symbol {
/**
* **Darts count opposite color cells in that direction**
*
* @param x - The x-coordinate of the symbol.
* @param y - The y-coordinate of the symbol.
*/
constructor(x, y) {
super(x, y);
Object.defineProperty(this, "x", {
enumerable: true,
configurable: true,
writable: true,
value: x
});
Object.defineProperty(this, "y", {
enumerable: true,
configurable: true,
writable: true,
value: y
});
Object.defineProperty(this, "linkedDirections", {
enumerable: true,
configurable: true,
writable: true,
value: {
[Direction.Left]: Direction.Left,
[Direction.Up]: Direction.Up,
[Direction.Right]: Direction.Right,
[Direction.Down]: Direction.Down,
}
});
}
changeDirections(linkedDirections) {
this.linkedDirections = linkedDirections;
return this;
}
get id() {
return `linkedDirection`;
}
get explanation() {
return `Direction pointed by this symbol should *behave* the same.`;
}
get configs() {
return DirectionLinkerSymbol.CONFIGS;
}
createExampleGrid() {
return DirectionLinkerSymbol.EXAMPLE_GRID;
}
deltaCoordinate(c, direction) {
return {
x: c.x + DirectionLinkerSymbol.directionDeltas[direction].dx,
y: c.y + DirectionLinkerSymbol.directionDeltas[direction].dy,
};
}
validateSymbol(grid) {
// A turtle is an object which have 2 coordinates
// This turtle when activated will check the color of both the cells it is pointing to
// If the color of one of the cell is gray, the turtle doesn't fire any other turtle
// If one of the turtle's coordinate is out of the grid, consider it's color as opposite of the base color
// If the color of both cells are different, return a State.Error
// If the color of both are the same as the color of the turtle, the turtle fires another turtle in all the directions according to the linked directions except the one it came from and the directions where a turtle has already been fired
// If the color of both are the opposite of the turtle, the turtle doesn't fire any other turtle
// If no turtle remains, go to final state
// Final state is State.Satisfied if no gray cell is found, State.Incomplete otherwise
let checkedCouples = this.getInitialCheckedCouples(this.x, this.y, grid);
if (checkedCouples.some(({ color1, color2 }) => (color1 === null && color2 !== null) ||
(color1 !== null && color2 === null))) {
return State.Error;
}
if (checkedCouples.some(({ color1, color2 }) => color1 === Color.Gray || color2 === Color.Gray)) {
return State.Incomplete;
}
checkedCouples = checkedCouples.filter(({ color1, color2 }) => color1 !== null && color2 !== null);
const queue = checkedCouples.slice();
let grayFound = false;
while (queue.length > 0) {
const turtle = queue.shift();
const { pos1, pos2, color1: baseColor1, color2: baseColor2 } = turtle;
const color1 = getColor(pos1, grid);
const color2 = getColor(pos2, grid);
if (color1 === Color.Gray || color2 === Color.Gray) {
grayFound = true;
}
else if (color1 === null || color2 === null) {
if ((color1 === null && color2 === baseColor2) ||
(color2 === null && color1 === baseColor1)) {
return State.Error;
}
}
else if (color1 !== baseColor1 || color2 !== baseColor2) {
if (color1 === baseColor1 || color2 === baseColor2) {
return State.Error;
}
}
if (color1 === baseColor1) {
const directions = Object.keys(this.linkedDirections);
for (const direction of directions) {
const newTurtle = {
pos1: this.deltaCoordinate(pos1, direction),
pos2: this.deltaCoordinate(pos2, this.linkedDirections[direction]),
color1: baseColor1,
color2: baseColor2,
};
const newArrTurtle = {
pos1: grid.toArrayCoordinates(newTurtle.pos1.x, newTurtle.pos1.y),
pos2: grid.toArrayCoordinates(newTurtle.pos2.x, newTurtle.pos2.y),
color1: baseColor1,
color2: baseColor2,
};
if (checkedCouples.some(({ pos1, pos2 }) => pos1.x === newArrTurtle.pos1.x &&
pos1.y === newArrTurtle.pos1.y &&
pos2.x === newArrTurtle.pos2.x &&
pos2.y === newArrTurtle.pos2.y) ||
(pos1.x === newArrTurtle.pos2.x &&
pos1.y === newArrTurtle.pos2.y &&
pos2.x === newArrTurtle.pos1.x &&
pos2.y === newArrTurtle.pos1.y)) {
continue;
}
checkedCouples.push(newArrTurtle);
queue.push(newTurtle);
}
}
}
return grayFound ? State.Incomplete : State.Satisfied;
}
copyWith({ x, y }) {
return new DirectionLinkerSymbol(x ?? this.x, y ?? this.y);
}
getInitialCheckedCouples(x, y, grid) {
// 1x1
if (x % 1 === 0 && y % 1 === 0) {
return [makeTurtle({ x, y }, { x, y }, grid)];
}
// 1x2
if (x % 1 === 0) {
if (this.linkedDirections[Direction.Up] === Direction.Up &&
this.linkedDirections[Direction.Down] === Direction.Down) {
return [
makeTurtle({ x, y: y - 0.5 }, { x, y: y - 0.5 }, grid),
makeTurtle({ x, y: y + 0.5 }, { x, y: y + 0.5 }, grid),
];
}
else {
return [makeTurtle({ x, y: y - 0.5 }, { x, y: y + 0.5 }, grid)];
}
}
// 2x1
if (y % 1 === 0) {
if (this.linkedDirections[Direction.Left] === Direction.Left &&
this.linkedDirections[Direction.Right] === Direction.Right) {
return [
makeTurtle({ x: x - 0.5, y }, { x: x - 0.5, y }, grid),
makeTurtle({ x: x + 0.5, y }, { x: x + 0.5, y }, grid),
];
}
else {
return [makeTurtle({ x: x - 0.5, y }, { x: x + 0.5, y }, grid)];
}
}
// 2x2
if (this.linkedDirections[Direction.Left] === Direction.Left &&
this.linkedDirections[Direction.Right] === Direction.Right) {
return [
makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x - 0.5, y: y + 0.5 }, grid),
makeTurtle({ x: x + 0.5, y: y - 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
];
}
if (this.linkedDirections[Direction.Up] === Direction.Up &&
this.linkedDirections[Direction.Down] === Direction.Down) {
return [
makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x + 0.5, y: y - 0.5 }, grid),
makeTurtle({ x: x - 0.5, y: y + 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
];
}
else if ((this.linkedDirections[Direction.Up] === Direction.Left &&
this.linkedDirections[Direction.Left] === Direction.Up) ||
(this.linkedDirections[Direction.Up] === Direction.Right &&
this.linkedDirections[Direction.Right] === Direction.Up)) {
return [
makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x - 0.5, y: y - 0.5 }, grid),
];
}
return [
makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
makeTurtle({ x: x - 0.5, y: y + 0.5 }, { x: x + 0.5, y: y - 0.5 }, grid),
];
}
}
Object.defineProperty(DirectionLinkerSymbol, "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,
},
])
});
Object.defineProperty(DirectionLinkerSymbol, "EXAMPLE_GRID", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze(GridData.create(['wwbbw', 'wwwww', 'wwwww', 'wwwww']))
});
Object.defineProperty(DirectionLinkerSymbol, "directionDeltas", {
enumerable: true,
configurable: true,
writable: true,
value: {
[Direction.Up]: { dx: 0, dy: -1 },
[Direction.Down]: { dx: 0, dy: 1 },
[Direction.Left]: { dx: -1, dy: 0 },
[Direction.Right]: { dx: 1, dy: 0 },
}
});
export default DirectionLinkerSymbol;
export const instance = undefined;