@logic-pad/core
Version:
173 lines (172 loc) • 6.25 kB
JavaScript
import { ConfigType } from '../config.js';
import { array } from '../dataHelper.js';
import GridData, { NEIGHBOR_OFFSETS } from '../grid.js';
import GridZones from '../gridZones.js';
import { Color, State } from '../primitives.js';
import Rule from './rule.js';
class CellCountPerZoneRule extends Rule {
/**
* **Zones of the same size have the same number of <color> cells.**
*
* @param color - The color of the cells to count.
*/
constructor(color) {
super();
Object.defineProperty(this, "color", {
enumerable: true,
configurable: true,
writable: true,
value: color
});
this.color = color;
}
get id() {
return `zone_cell_count`;
}
get explanation() {
return `Zones of the same size have the same number of ${this.color} cells`;
}
get configs() {
return CellCountPerZoneRule.CONFIGS;
}
createExampleGrid() {
if (this.color === Color.Light) {
return CellCountPerZoneRule.EXAMPLE_GRID_LIGHT;
}
else if (this.color === Color.Dark) {
return CellCountPerZoneRule.EXAMPLE_GRID_DARK;
}
else {
return CellCountPerZoneRule.EXAMPLE_GRID_GRAY;
}
}
get searchVariants() {
return CellCountPerZoneRule.SEARCH_VARIANTS;
}
validateGrid(grid) {
let complete = true;
const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
const zones = [];
while (true) {
const seed = grid.find((_tile, x, y) => !visited[y][x]);
if (!seed)
break;
const zone = {
positions: [],
completed: 0,
possible: 0,
};
const stack = [seed];
while (stack.length > 0) {
let { x, y } = stack.pop();
({ x, y } = grid.toArrayCoordinates(x, y));
if (visited[y][x])
continue;
visited[y][x] = true;
zone.positions.push({ x, y });
if (grid.getTile(x, y).color === this.color) {
zone.completed++;
}
else if (grid.getTile(x, y).color === Color.Gray) {
zone.possible++;
complete = false;
}
for (const offset of NEIGHBOR_OFFSETS) {
const next = grid.toArrayCoordinates(x + offset.x, y + offset.y);
if (!grid.zones.edges.some(e => {
const { x: x1, y: y1 } = grid.toArrayCoordinates(e.x1, e.y1);
const { x: x2, y: y2 } = grid.toArrayCoordinates(e.x2, e.y2);
return ((x1 === x && y1 === y && x2 === next.x && y2 === next.y) ||
(x2 === x && y2 === y && x1 === next.x && y1 === next.y));
})) {
const nextTile = grid.getTile(next.x, next.y);
if (nextTile.exists) {
stack.push(next);
}
}
}
}
zones.push(zone);
}
if (zones.length <= 1) {
return { state: complete ? State.Satisfied : State.Incomplete };
}
else {
const errorZone = zones.find(z => zones.some(zz => zz !== z &&
zz.positions.length === z.positions.length &&
(zz.completed > z.completed + z.possible ||
zz.completed + zz.possible < z.completed)));
if (errorZone) {
return {
state: State.Error,
positions: errorZone.positions,
};
}
else {
return { state: complete ? State.Satisfied : State.Incomplete };
}
}
}
copyWith({ color }) {
return new CellCountPerZoneRule(color ?? this.color);
}
withColor(color) {
return this.copyWith({ color });
}
}
Object.defineProperty(CellCountPerZoneRule, "CONFIGS", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze([
{
type: ConfigType.Color,
default: Color.Light,
allowGray: true,
field: 'color',
description: 'Color',
configurable: true,
},
])
});
Object.defineProperty(CellCountPerZoneRule, "EXAMPLE_GRID_LIGHT", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze(GridData.create(['bwbbb', 'wbbwb', 'bbbwb', 'bwbwb'])
.withZones(new GridZones([
{ x1: 0, y1: 1, x2: 0, y2: 2 },
{ x1: 1, y1: 1, x2: 1, y2: 2 },
{ x1: 2, y1: 1, x2: 2, y2: 2 },
{ x1: 3, y1: 1, x2: 3, y2: 2 },
{ x1: 4, y1: 1, x2: 4, y2: 2 },
{ x1: 1, y1: 0, x2: 2, y2: 0 },
{ x1: 1, y1: 1, x2: 2, y2: 1 },
{ x1: 2, y1: 2, x2: 3, y2: 2 },
{ x1: 2, y1: 3, x2: 3, y2: 3 },
]))
.addRule(new CellCountPerZoneRule(Color.Light)))
});
Object.defineProperty(CellCountPerZoneRule, "EXAMPLE_GRID_DARK", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze(CellCountPerZoneRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))))
});
Object.defineProperty(CellCountPerZoneRule, "EXAMPLE_GRID_GRAY", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze(CellCountPerZoneRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Light ? Color.Gray : tile.color)))))
});
Object.defineProperty(CellCountPerZoneRule, "SEARCH_VARIANTS", {
enumerable: true,
configurable: true,
writable: true,
value: [
new CellCountPerZoneRule(Color.Light).searchVariant(),
new CellCountPerZoneRule(Color.Dark).searchVariant(),
]
});
export default CellCountPerZoneRule;
export const instance = new CellCountPerZoneRule(Color.Light);