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 ``;
}
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);
}