UNPKG

@logic-pad/core

Version:
106 lines (105 loc) 3.54 kB
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; }