@logic-pad/core
Version:
106 lines (105 loc) • 3.54 kB
JavaScript
import { z } from 'zod';
import GridData from './grid.js';
import { Color, PuzzleType, State } from './primitives.js';
export const MetadataSchema = z.strictObject({
title: z.string('Title must be a string').min(1, 'Title must not be empty'),
author: z
.string('Author must be a string')
.min(1, 'Author must not be empty'),
description: z.string('Description must be a string'),
difficulty: z
.number('Difficulty must be a number')
.int('Difficulty must be an integer')
.min(0, 'Difficulty must be at least 0')
.max(10, 'Difficulty must be at most 10'),
}, 'Data must be an object');
export const PuzzleSchema = MetadataSchema.extend({
grid: z.instanceof(GridData, { error: 'Grid must be a GridData instance' }),
solution: z
.instanceof(GridData, { error: 'Solution must be a GridData instance' })
.nullable(),
}).strict();
/**
* Checks if two puzzles are equal.
*/
export function puzzleEquals(a, b) {
return (a.title === b.title &&
a.author === b.author &&
a.description === b.description &&
a.difficulty === b.difficulty &&
a.grid.equals(b.grid) &&
((a.solution === null && b.solution === null) ||
(!!a.solution && !!b.solution && a.solution.equals(b.solution))));
}
/**
* Get the types of a puzzle based on its grid properties. The returned types are ordered by their priority.
* The first type is the most important one.
*/
export function getPuzzleTypes(grid) {
const types = [];
let logic = true;
if (grid.musicGrid.value) {
types.push(PuzzleType.Music);
logic = false;
}
if (grid.completePattern.value) {
types.push(PuzzleType.Pattern);
logic = false;
}
if (grid.underclued.value) {
types.push(PuzzleType.Underclued);
}
if (logic) {
types.push(PuzzleType.Logic);
}
return types;
}
export function validatePuzzleChecklist(metadata, gridWithSolution, state) {
const checklist = {
items: [],
isValid: true,
};
checklist.items.push({
id: 'metadataValid',
success: MetadataSchema.safeParse(metadata).success,
mandatory: true,
});
checklist.items.push({
id: 'gridValid',
success: gridWithSolution.width > 0 && gridWithSolution.height > 0,
mandatory: true,
});
if (gridWithSolution.requireSolution()) {
checklist.items.push({
id: 'autoValidation',
success: false,
mandatory: false,
});
checklist.items.push({
id: 'solutionIsNotEmpty',
success: gridWithSolution.musicGrid.value
? gridWithSolution.tiles.some(row => row.some(tile => !tile.fixed && tile.color === Color.Dark))
: gridWithSolution.tiles.some(row => row.some(tile => !tile.fixed && tile.color !== Color.Gray)),
mandatory: true,
});
}
else {
checklist.items.push({
id: 'autoValidation',
success: true,
mandatory: false,
});
checklist.items.push({
id: 'solutionIsComplete',
success: gridWithSolution.isComplete(),
mandatory: true,
});
checklist.items.push({
id: 'solutionIsValid',
success: state.final !== State.Error,
mandatory: true,
});
}
checklist.isValid = !checklist.items.some(x => !x.success && x.mandatory);
return checklist;
}