import { IReconstruction, PuzzleType } from "./constants"; import { Interpreter } from "./scramble-interpreter"; import { ScrambleParser } from "./scramble-parser"; import { newArr } from "./utils"; export function defaultInner(s: string, withSuffix = true) { return s.replace(/\n/g, "
") + (withSuffix ? "
" : ""); } type IToken = ReturnType["program"]; export function getTreeString(token: IToken, puzzle: PuzzleType): string { const { value } = token; switch (token.type) { case "Move": { if (puzzle === "square1" && token.value != "/") { const regs = [ /^(\()(\s*)(-?\d)(,)(\s*)(-?\d)(\s*)(\))/, /^(-?\d)(,)(\s*)(-?\d)/, /^(-?\d)(-?\d)/, /^(-?\d)/, ]; const operators = /^[(,)]$/; for (let i = 0, maxi = regs.length; i < maxi; i += 1) { const m = regs[i].exec(value); if (m) { return m .slice(1) .map((s) => operators.test(s) ? `${s}` : /\d$/.test(s) ? s === "0" ? `${s}` : `${s}` : defaultInner(s, false), ) .join(""); } } } return `${defaultInner( value, false, )}`; } case "Comment": { return `${defaultInner( value, false, )}`; } case "Space": { return defaultInner(value, false); } case "Expression": { return (value as IToken[]).map((t) => getTreeString(t, puzzle)).join(""); } case "ParentesizedExpression": { return ( `(` + getTreeString(value.expr, puzzle) + `)` + (value.cant != 1 || value.explicit ? `${value.cant}` : "") ); } case "ConmutatorExpression": { if (value.setup) { return ( `[` + getTreeString(value.setup, puzzle) + `:` + getTreeString(value.conmutator, puzzle) + `]` + (value.cant != 1 || value.explicit ? `${value.cant}` : "") ); } return ( `[` + getTreeString(value.expr1, puzzle) + `,` + getTreeString(value.expr2, puzzle) + `]` + (value.cant != 1 || value.explicit ? `${value.cant}` : "") ); } } return ""; } function getMoveLength( sequence: string[], puzzle: PuzzleType, order: number, ): number { try { switch (puzzle) { case "rubik": case "mirror": case "void": { return sequence.reduce( (acc: any[], e) => [ ...acc, ...ScrambleParser.parseNNN(e, { a: order, b: order, c: order }), ], [], ).length; } case "skewb": { return sequence.reduce( (acc: any[], e) => [...acc, ...ScrambleParser.parseSkewb(e)], [], ).length; } case "square1": { return sequence.reduce( (acc: any[], e) => [...acc, ...ScrambleParser.parseSquare1(e)], [], ).length; } case "megaminx": { return sequence.reduce( (acc: any[], e) => [...acc, ...ScrambleParser.parseMegaminx(e)], [], ).length; } case "pyraminx": { return sequence.reduce( (acc: any[], e) => [...acc, ...ScrambleParser.parsePyraminx(e)], [], ).length; } case "clock": { return ScrambleParser.parseClock(sequence.join(" ")).length; } case "masterskewb": { return ScrambleParser.parseSkewb(sequence.join(" ")).length; } case "helicopter": { return sequence.reduce( (acc: any[], e) => [...acc, ...e.split(/\s+/)], [], ).length; } } } catch { void 0; } return 0; } export function parseReconstruction( s: string, puzzle: PuzzleType, order: number, ): IReconstruction { const itp = new Interpreter(false, puzzle); let errorCursor = -1; try { const tree = itp.getTree(s); if (tree.error) { errorCursor = typeof tree.cursor === "number" ? tree.cursor : 0; } else { const program = itp.getFlat(tree.program); const flat = program.filter((token) => token.cursor >= 0); const sequence: string[] = flat.map((token) => token.value); let sequenceIndex: number[] = []; const finalAlpha = getMoveLength(sequence, puzzle, order); switch (puzzle) { case "square1": { sequenceIndex = newArr(finalAlpha) .fill(0) .map((_, i) => i); break; } default: { sequenceIndex = flat.map((token) => token.cursor); } } return { result: getTreeString(tree.program.value, puzzle) + "
", finalAlpha, sequence, sequenceIndex, hasError: false, }; } } catch (e) { if (typeof e === "number") { errorCursor = e; } } if (errorCursor != -1) { const pref = defaultInner(s.slice(0, errorCursor), false); let middle = ""; const match = /^([^\s\n]+)/.exec(s.slice(errorCursor)); if (match) { middle = `${match[0]}`; return { result: pref + middle + defaultInner(s.slice(errorCursor + match[0].length)), finalAlpha: 0, sequence: [], sequenceIndex: [], hasError: true, }; } } return { result: defaultInner(s), finalAlpha: 0, sequence: [], sequenceIndex: [], hasError: false, }; } export function prettyScramble(scramble: string): string { return scramble .trim() .replace(/\s*
\s*/g, "\n") .replace(/(\n\s+)/g, "\n"); } export function randomUUID() { if (crypto && crypto.randomUUID) { return crypto.randomUUID(); } const lens = [8, 4, 4, 4, 12]; const res: string[][] = []; for (let i = 0; i < 5; i += 1) { res[i] = []; for (let j = 0; j < lens[i]; j += 1) { res[i].push(Math.random().toString(16).slice(2)); } } return res.map((s) => s.join("")).join("-"); } export function randomCSSId() { return btoa(randomUUID()).slice(0, 16); }