@logic-pad/core
Version:
170 lines (169 loc) • 5.61 kB
JavaScript
import GridData from '../grid.js';
import { Color, State, Mode, } from '../primitives.js';
import Rule from './rule.js';
import CustomIconSymbol from '../symbols/customIconSymbol.js';
import validateGrid from '../validate.js';
class PerfectionRule extends Rule {
/**
* **Quest for Perfection: cell colors are final**
*
* @param editor - whether to enable editor mode. This field is automatically set by the editor.
*/
constructor(editor = false) {
super();
Object.defineProperty(this, "editor", {
enumerable: true,
configurable: true,
writable: true,
value: editor
});
this.editor = editor;
}
get id() {
return `perfection`;
}
get explanation() {
return `*Quest for Perfection*: cell colors are final`;
}
get configs() {
return null;
}
createExampleGrid() {
return PerfectionRule.EXAMPLE_GRID;
}
get searchVariants() {
return PerfectionRule.SEARCH_VARIANTS;
}
get necessaryForCompletion() {
return false;
}
get isSingleton() {
return true;
}
modeVariant(mode) {
// only allow this rule in perfection mode
if (this.editor === (mode === Mode.Create)) {
return this;
}
else if (mode === Mode.Create) {
return this.copyWith({ editor: true });
}
else {
return this.copyWith({ editor: false });
}
}
validateGrid(grid) {
if (grid.getTileCount(true, undefined, Color.Gray) > 0) {
return { state: State.Incomplete };
}
else {
return { state: State.Satisfied };
}
}
/**
* If the grid passes validation but is different from the solution, indicate the error in the final state.
*/
onFinalValidation(grid, solution, state) {
if (state.final === State.Error)
return state;
if (solution === null)
return state;
const positions = [];
grid.tiles.forEach((row, y) => row.forEach((t, x) => {
if (t.exists &&
t.color !== Color.Gray &&
t.color !== solution.getTile(x, y).color) {
positions.push({ x, y });
}
}));
if (positions.length > 0) {
const ruleId = grid.rules.indexOf(this);
return {
final: State.Error,
rules: state.rules.map((r, idx) => {
if (idx === ruleId) {
return { state: State.Error, positions };
}
else {
return r;
}
}),
symbols: state.symbols,
};
}
return state;
}
fixTiles(grid, exclusions) {
if (grid.getTileCount(true, false, Color.Light) > 0 ||
grid.getTileCount(true, false, Color.Dark) > 0) {
return grid.withTiles(tiles => tiles.map((row, y) => row.map((t, x) => t.exists &&
t.color !== Color.Gray &&
!exclusions?.some(e => e.x === x && e.y === y)
? t.withFixed(true)
: t)));
}
return grid;
}
isValid(grid, solution) {
return validateGrid(grid, solution).final !== State.Error;
}
findSingleError(grid, solution) {
if (solution === null)
return [];
const positions = [];
// If a solution is available, we can compare against the solution and allow the user to modify the one single error.
grid.tiles.forEach((row, y) => row.forEach((t, x) => {
if (t.exists &&
t.color !== Color.Gray &&
t.color !== solution.getTile(x, y).color) {
positions.push({ x, y });
}
}));
if (positions.length > 1) {
const connected = grid.connections.getConnectedTiles(positions[0]);
if (!positions.every(p => connected.some(c => c.x === p.x && c.y === p.y))) {
return [];
}
}
return positions;
}
/**
* Force all tiles to be fixed.
*
* If the grid is already wrong, prevent the player from changing it further.
*/
onSetGrid(oldGrid, newGrid, solution) {
if (this.editor)
return newGrid;
const oldGridIsValid = this.isValid(oldGrid, solution);
const newGridIsValid = this.isValid(newGrid, solution);
if (!oldGridIsValid && !newGridIsValid) {
const oldPositions = this.findSingleError(oldGrid, solution);
return this.fixTiles(oldGrid, oldPositions);
}
else if (!newGridIsValid) {
const positions = this.findSingleError(newGrid, solution);
return this.fixTiles(newGrid, positions);
}
return this.fixTiles(newGrid);
}
copyWith({ editor }) {
return new PerfectionRule(editor ?? this.editor);
}
}
Object.defineProperty(PerfectionRule, "EXAMPLE_GRID", {
enumerable: true,
configurable: true,
writable: true,
value: Object.freeze(GridData.create(['w']).addSymbol(new CustomIconSymbol('', GridData.create(['w']), 0, 0, 'MdStars')))
});
Object.defineProperty(PerfectionRule, "SEARCH_VARIANTS", {
enumerable: true,
configurable: true,
writable: true,
value: [
new PerfectionRule().searchVariant(),
]
});
export default PerfectionRule;
export const instance = new PerfectionRule();