/** * Copyright (C) 2023 Shuang Chen This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----------------------------------------------------------------------- Modified by Isaac Vega */ import { circle, rn, rndEl } from "../lib/mathlib"; import { formatScramble, mega, regScrambler } from "./scramble"; const cubesuff = ["", "2", "'"]; const minxsuff = ["", "2", "'", "2'"]; let seq = []; let p = []; function adjScramble(faces, adj, len, suffixes?) { if (suffixes == undefined) { suffixes = [""]; } let used = 0; let face; const ret = []; for (let j = 0; j < len; j++) { do { face = rn(faces.length); } while ((used >> face) & 1); ret.push(faces[face] + rndEl(suffixes)); used &= ~adj[face]; used |= 1 << face; } return ret.join(" "); } function yj4x4(type, len) { // the idea is to keep the fixed center on U and do Rw or Lw, Fw or Bw, to not disturb it const turns = [ ["U", "D"], ["R", "L", "r"], ["F", "B", "f"], ]; const donemoves = []; let lastaxis; let fpos = 0; // 0 = Ufr, 1 = Ufl, 2 = Ubl, 3 = Ubr let j, k; let s = ""; lastaxis = -1; for (j = 0; j < len; j++) { let done = 0; do { const first = rn(turns.length); const second = rn(turns[first].length); if (first != lastaxis || donemoves[second] == 0) { if (first == lastaxis) { donemoves[second] = 1; const rs = rn(cubesuff.length); if (first == 0 && second == 0) { fpos = (fpos + 4 + rs) % 4; } if (first == 1 && second == 2) { // r or l if (fpos == 0 || fpos == 3) s += "l" + cubesuff[rs] + " "; else s += "r" + cubesuff[rs] + " "; } else if (first == 2 && second == 2) { // f or b if (fpos == 0 || fpos == 1) s += "b" + cubesuff[rs] + " "; else s += "f" + cubesuff[rs] + " "; } else { s += turns[first][second] + cubesuff[rs] + " "; } } else { for (k = 0; k < turns[first].length; k++) { donemoves[k] = 0; } lastaxis = first; donemoves[second] = 1; const rs = rn(cubesuff.length); if (first == 0 && second == 0) { fpos = (fpos + 4 + rs) % 4; } if (first == 1 && second == 2) { // r or l if (fpos == 0 || fpos == 3) s += "l" + cubesuff[rs] + " "; else s += "r" + cubesuff[rs] + " "; } else if (first == 2 && second == 2) { // f or b if (fpos == 0 || fpos == 1) s += "b" + cubesuff[rs] + " "; else s += "f" + cubesuff[rs] + " "; } else { s += turns[first][second] + cubesuff[rs] + " "; } } done = 1; } } while (done == 0); } return s; } regScrambler("444yj", yj4x4); export function bicube(type, len) { function canMove(face) { let u = [], i, j, done, z = 0; for (i = 0; i < 9; i++) { done = 0; for (j = 0; j < u.length; j++) { if (u[j] == start[d[face][i]]) done = 1; } if (done == 0) { u[u.length] = start[d[face][i]]; if (start[d[face][i]] == 0) z = 1; } } return u.length == 5 && z == 1; } function doMove(face, amount) { for (let i = 0; i < amount; i++) { let t = start[d[face][0]]; start[d[face][0]] = start[d[face][6]]; start[d[face][6]] = start[d[face][4]]; start[d[face][4]] = start[d[face][2]]; start[d[face][2]] = t; t = start[d[face][7]]; start[d[face][7]] = start[d[face][5]]; start[d[face][5]] = start[d[face][3]]; start[d[face][3]] = start[d[face][1]]; start[d[face][1]] = t; } } const d = [ [0, 1, 2, 5, 8, 7, 6, 3, 4], [6, 7, 8, 13, 20, 19, 18, 11, 12], [0, 3, 6, 11, 18, 17, 16, 9, 10], [8, 5, 2, 15, 22, 21, 20, 13, 14], ]; let start = [ 1, 1, 2, 3, 3, 2, 4, 4, 0, 5, 6, 7, 8, 9, 10, 10, 5, 6, 7, 8, 9, 11, 11, ], move = "UFLR", s = "", arr = [], poss, done, i, j, x, y; while (arr.length < len) { poss = [1, 1, 1, 1]; for (j = 0; j < 4; j++) { if (poss[j] == 1 && !canMove(j)) poss[j] = 0; } done = 0; while (done == 0) { x = rn(4); if (poss[x] == 1) { y = rn(3) + 1; doMove(x, y); done = 1; } } arr[arr.length] = [x, y]; if (arr.length >= 2) { if (arr[arr.length - 1][0] == arr[arr.length - 2][0]) { arr[arr.length - 2][1] = (arr[arr.length - 2][1] + arr[arr.length - 1][1]) % 4; arr = arr.slice(0, arr.length - 1); } } if (arr.length >= 1) { if (arr[arr.length - 1][1] == 0) { arr = arr.slice(0, arr.length - 1); } } } for (i = 0; i < len; i++) { s += move[arr[i][0]] + cubesuff[arr[i][1] - 1] + " "; } return s; } regScrambler("bic", bicube); // Clock functions. function c(s) { const array = [ s + "=0", s + "+1", s + "+2", s + "+3", s + "+4", s + "+5", s + "+6", s + "-5", s + "-4", s + "-3", s + "-2", s + "-1", ]; return " " + rndEl(array) + " "; } function c2() { return rndEl(["U", "d"]) + rndEl(["U", "d"]); } function c3() { return " "; } function do15puzzle(mirrored, len, arrow?, tiny?) { const moves = mirrored ? ["U", "L", "R", "D"] : ["D", "R", "L", "U"]; const effect = [ [0, -1], [1, 0], [-1, 0], [0, 1], ]; let x = 0, y = 3, r, lastr = 5, ret = []; for (let i = 0; i < len; i++) { do { r = rn(4); } while ( x + effect[r][0] < 0 || x + effect[r][0] > 3 || y + effect[r][1] < 0 || y + effect[r][1] > 3 || r + lastr == 3 ); x += effect[r][0]; y += effect[r][1]; if (ret.length > 0 && ret[ret.length - 1][0] == r) { ret[ret.length - 1][1]++; } else { ret.push([r, 1]); } lastr = r; } let retstr = ""; for (let i = 0; i < ret.length; i++) { let m = mirrored ? ret[i][0] : 3 - ret[i][0]; m = (arrow ? "\uFFEA\uFFE9\uFFEB\uFFEC" : "ULRD").charAt(m); if (tiny) { retstr += m + (ret[i][1] == 1 ? "" : ret[i][1]) + " "; } else { for (let j = 0; j < ret[i][1]; j++) { retstr += m + " "; } } } return retstr; } export function pochscramble(x: number, y: number) { let ret = ""; let i, j; for (i = 0; i < y; i++) { ret += " "; for (j = 0; j < x; j++) { ret += (j % 2 == 0 ? "R" : "D") + rndEl(["++", "--"]) + " "; } ret += "U" + (ret.endsWith("-- ") ? "'\\n" : "\\n"); } return ret; } function carrotscramble(x, y) { let ret = ""; let i, j; for (i = 0; i < y; i++) { ret += " "; for (j = 0; j < x / 2; j++) { ret += rndEl(["+", "-"]) + rndEl(["+", "-"]) + " "; } ret += "U" + rndEl(["'\\n", "\\n"]); } return ret; } function gigascramble(len) { let ret = ""; let i, j; for (i = 0; i < Math.ceil(len / 10); i++) { ret += " "; for (j = 0; j < 10; j++) { ret += (j % 2 == 0 ? "Rr".charAt(rn(2)) : "Dd".charAt(rn(2))) + rndEl(["+ ", "++", "- ", "--"]) + " "; } ret += "y" + rndEl(minxsuff) + "\n"; } return ret; } function sq1_scramble(type, len) { seq = []; let i, k; sq1_getseq(1, type, len); let s = ""; for (i = 0; i < seq[0].length; i++) { k = seq[0][i]; if (k[0] == 7) { s += "/"; } else { s += " (" + k[0] + "," + k[1] + ") "; } } return s; } function ssq1t_scramble(len) { seq = []; let i; sq1_getseq(2, 0, len); let s = seq[0], t = seq[1], u = ""; if (s[0][0] == 7) s = [[0, 0]].concat(s); if (t[0][0] == 7) t = [[0, 0]].concat(t); for (i = 0; i < len; i++) { u += "(" + s[2 * i][0] + "," + t[2 * i][0] + "," + t[2 * i][1] + "," + s[2 * i][1] + ") / "; } return u; } function sq1_getseq(num, type, len) { for (let n = 0; n < num; n++) { p = [ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, ]; seq[n] = []; let cnt = 0; while (cnt < len) { const x = rn(12) - 5; const y = type == 2 ? 0 : rn(12) - 5; const size = (x == 0 ? 0 : 1) + (y == 0 ? 0 : 1); if ((cnt + size <= len || type != 1) && (size > 0 || cnt == 0)) { if (sq1_domove(x, y)) { if (type == 1) cnt += size; if (size > 0) seq[n][seq[n].length] = [x, y]; if (cnt < len || type != 1) { cnt++; seq[n][seq[n].length] = [7, 0]; sq1_domove(7, 0); } } } } } } function sq1_domove(x, y) { let i, px, py; if (x == 7) { for (i = 0; i < 6; i++) { circle(p, i + 6, i + 12); } return true; } else { if ( p[(17 - x) % 12] || p[(11 - x) % 12] || p[12 + ((17 - y) % 12)] || p[12 + ((11 - y) % 12)] ) { return false; } else { // do the move itself px = p.slice(0, 12); py = p.slice(12, 24); for (i = 0; i < 12; i++) { p[i] = px[(12 + i - x) % 12]; p[i + 12] = py[(12 + i - y) % 12]; } return true; } } } function moyuRedi(length) { const ret = []; for (let i = 0; i < length; i++) { ret.push(mega([["R"], ["L"]], ["", "'"], 3 + rn(3))); } return ret.join(" x "); } function addPyrTips(scramble, moveLen) { let cnt = 0; const rnd: any[] = []; for (let i = 0; i < 4; i++) { rnd[i] = rn(3); if (rnd[i] > 0) { rnd[i] = "ulrb".charAt(i) + ["! ", "' "][rnd[i] - 1]; cnt++; } else { rnd[i] = ""; } } return ( scramble.substr(0, scramble.length - moveLen * cnt) + " " + rnd.join("") ); } export function utilscramble(type: string, len: number) { let ret = ""; switch (type) { case "15p": // 15 puzzle return do15puzzle(false, len); case "15pm": // 15 puzzle, mirrored return do15puzzle(true, len); case "15pat": // 15 puzzle return do15puzzle(false, len, true, true); case "clkwca": // Clock (WCA Notation) const clkapp = [ "0+", "1+", "2+", "3+", "4+", "5+", "6+", "1-", "2-", "3-", "4-", "5-", ]; ret = "UR? DR? DL? UL? U? R? D? L? ALL? y2 U? R? D? L? ALL?????"; for (let i = 0; i < 14; i++) { ret = ret.replace("?", rndEl(clkapp)); } return ret .replace("?", rndEl(["", " UR"])) .replace("?", rndEl(["", " DR"])) .replace("?", rndEl(["", " DL"])) .replace("?", rndEl(["", " UL"])); case "clk": // Clock (Jaap order) return ( "UU" + c("u") + "dU" + c("u") + "dd" + c("u") + "Ud" + c("u") + "dU" + c("u") + "Ud" + c("u") + "UU" + c("u") + "UU" + c("u") + "UU" + c("u") + "dd" + c3() + c2() + "\\ndd" + c("d") + "dU" + c("d") + "UU" + c("d") + "Ud" + c("d") + "UU" + c3() + "UU" + c3() + "Ud" + c3() + "dU" + c3() + "UU" + c3() + "dd" + c("d") + c2() ); case "clkc": // Clock (concise) ret = ""; for (let i = 0; i < 4; i++) ret += "(" + (rn(12) - 5) + ", " + (rn(12) - 5) + ") / "; for (let i = 0; i < 6; i++) ret += "(" + (rn(12) - 5) + ") / "; for (let i = 0; i < 4; i++) ret += rndEl(["d", "U"]); return ret; case "clke": // Clock (efficient order) return ( "UU" + c("u") + "dU" + c("u") + "dU" + c("u") + "UU" + c("u") + "UU" + c("u") + "UU" + c("u") + "Ud" + c("u") + "Ud" + c("u") + "dd" + c("u") + "dd" + c3() + c2() + "\\nUU" + c3() + "UU" + c3() + "dU" + c("d") + "dU" + c3() + "dd" + c("d") + "Ud" + c3() + "Ud" + c("d") + "UU" + c3() + "UU" + c("d") + "dd" + c("d") + c2() ); case "giga": // Gigaminx return gigascramble(len); case "mgmo": // Megaminx (old style) return adjScramble( ["F", "B", "U", "D", "L", "DBR", "DL", "BR", "DR", "BL", "R", "DBL"], [ 0x554, 0xaa8, 0x691, 0x962, 0xa45, 0x58a, 0x919, 0x626, 0x469, 0x896, 0x1a5, 0x25a, ], len, ); case "klmp": // Kilominx (Pochmann) case "mgmp": // Megaminx (Pochmann) return pochscramble(10, Math.ceil(len / 10)); case "mgmc": // Megaminx (Carrot) return carrotscramble(10, Math.ceil(len / 10)); case "kilo": // Kilominx (Pochmann) return pochscramble(5, Math.ceil(len / 5)); case "heli": return adjScramble( [ "UF", "UR", "UB", "UL", "FR", "BR", "BL", "FL", "DF", "DR", "DB", "DL", ], [ 0x09a, 0x035, 0x06a, 0x0c5, 0x303, 0x606, 0xc0c, 0x909, 0xa90, 0x530, 0xa60, 0x5c0, ], len, ); case "redi": return adjScramble( ["L", "R", "F", "B", "l", "r", "f", "b"], [0x1c, 0x2c, 0x43, 0x83, 0xc1, 0xc2, 0x34, 0x38], len, ["", "'"], ); case "redim": return moyuRedi(len); case "pyrm": // Pyraminx (random moves) ret = mega([["U"], ["L"], ["R"], ["B"]], ["!", "'"], len); return addPyrTips(ret, 3).replace(/!/g, ""); case "prcp": // Pyraminx Crystal (Pochmann) return pochscramble(10, Math.ceil(len / 10)); case "mpyr": // Master Pyraminx ret = adjScramble( ["U!", "L!", "R!", "B!", "Uw", "Lw", "Rw", "Bw"], [0xe0, 0xd0, 0xb0, 0x70, 0xee, 0xdd, 0xbb, 0x77], len, ["!", "'"], ); return addPyrTips(ret, 4).replace(/!/g, ""); case "r3": // multiple 3x3x3 relay for (let i = 0; i < len; i++) { ret += (i == 0 ? "" : "\\n") + (i + 1) + ") ${333}"; } return formatScramble(ret); case "r3ni": // multiple 3x3x3 bld for (let i = 0; i < len; i++) { ret += (i == 0 ? "" : "\\n") + (i + 1) + ") ${333ni}"; } return formatScramble(ret); case "sq1h": // Square-1 (turn metric) return sq1_scramble(1, len); case "sq1t": // Square-1 (twist metric) return sq1_scramble(0, len); case "sq2": // Square-2 let i = 0; while (i < len) { const rndu = rn(12) - 5; const rndd = rn(12) - 5; if (rndu != 0 || rndd != 0) { i++; ret += "(" + rndu + "," + rndd + ") / "; } } return ret; case "ssq1t": // Super Square-1 (twist metric) return ssq1t_scramble(len); case "bsq": // Bandaged Square-1 return sq1_scramble(2, len); case "-1": // -1x-1x-1 (micro style) for (let i = 0; i < len; i++) { ret += String.fromCharCode(32 + rn(224)); } ret += "Error: subscript out of range"; return ret; case "333noob": // 3x3x3 for noobs ret = mega( [ ["turn the top face", "turn the bottom face"], ["turn the right face", "turn the left face"], ["turn the front face", "turn the back face"], ], " clockwise by 90 degrees,| counterclockwise by 90 degrees,| by 180 degrees,".split( "|", ), len, ).replace(/t/, "T"); return ret.substr(0, ret.length - 2) + "."; case "lol": // LOL ret = mega([["L"], ["O"]], 0, len); return ret.replace(/ /g, ""); } } regScrambler( [ "15p", "15pm", "15pat", "clkwca", "clk", "clkc", "clke", "giga", "mgmo", "mgmp", "mgmc", "kilo", "klmp", "heli", "redi", "redim", "pyrm", "prcp", "mpyr", "r3", "r3ni", "sq1h", "sq1t", "sq2", "ssq1t", "bsq", "-1", "333noob", "lol", ], utilscramble, );