@logic-pad/core
Version:
112 lines (111 loc) • 4.02 kB
JavaScript
import { ConfigType } from '../config.js';
import GridData from '../grid.js';
import { array, minBy } from '../dataHelper.js';
import { Color, State } from '../primitives.js';
import Rule from './rule.js';
import GridZones from '../gridZones.js';
export default class ConnectZonesRule extends Rule {
color;
title = 'Connect Zones';
static CONFIGS = Object.freeze([
{
type: ConfigType.Color,
default: Color.Light,
allowGray: false,
field: 'color',
description: 'Color',
configurable: true,
},
]);
static EXAMPLE_GRID_LIGHT = Object.freeze(GridData.create(['wbbwb', 'wbbwb', 'wbbww', 'wwbbb'])
.withZones(new GridZones([
{ x1: 2, y1: 1, x2: 2, 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 ConnectZonesRule(Color.Light)));
static EXAMPLE_GRID_DARK = Object.freeze(ConnectZonesRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))));
static SEARCH_VARIANTS = [
new ConnectZonesRule(Color.Light).searchVariant(),
new ConnectZonesRule(Color.Dark).searchVariant(),
];
/**
* **Connect all <color> cells in each zone**
*
* @param color - The color of the cells to connect.
*/
constructor(color) {
super();
this.color = color;
this.color = color;
}
get id() {
return `connect_zones`;
}
get explanation() {
return `Connect all ${this.color} cells in each zone`;
}
get configs() {
return ConnectZonesRule.CONFIGS;
}
createExampleGrid() {
return this.color === Color.Light
? ConnectZonesRule.EXAMPLE_GRID_LIGHT
: ConnectZonesRule.EXAMPLE_GRID_DARK;
}
get searchVariants() {
return ConnectZonesRule.SEARCH_VARIANTS;
}
validateGrid(grid) {
let complete = true;
let zoneId = 1;
const zoneMap = array(grid.width, grid.height, () => 0);
const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
const zones = grid.reduceByZone((zone, tile, x, y) => {
zoneMap[y][x] = zone;
if (tile.exists && tile.color === Color.Gray) {
complete = false;
}
return zone;
}, () => zoneId++);
for (const zone of zones) {
const islands = [];
while (true) {
const seed = grid.find((tile, x, y) => !visited[y][x] &&
zoneMap[y][x] === zone &&
tile.color === this.color);
if (!seed)
break;
const positions = [];
grid.iterateArea(seed, (tile, x, y) => {
if (tile.color !== this.color && tile.color !== Color.Gray) {
return false;
}
const pos = grid.toArrayCoordinates(x, y);
return zoneMap[pos.y][pos.x] === zone;
}, (tile, x, y) => {
if (tile.color === Color.Gray)
complete = false;
positions.push({ x, y });
}, visited);
islands.push(positions);
}
if (islands.length > 1) {
return {
state: State.Error,
positions: minBy(islands, island => island.length),
};
}
}
return { state: complete ? State.Satisfied : State.Incomplete };
}
copyWith({ color }) {
return new ConnectZonesRule(color ?? this.color);
}
withColor(color) {
return this.copyWith({ color });
}
}
export const instance = new ConnectZonesRule(Color.Dark);