/**
* 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 {
Cnk,
createMove,
createPrun,
edgeMove,
getNPerm,
getPruning,
rn,
rndPerm,
setNPerm,
valuedArray,
} from "./mathlib";
let permPrun, flipPrun, ecPrun, fullPrun;
const cmv = [];
const pmul = [];
const fmul = [];
const e1mv = [];
const c1mv = [];
const xxPrun01 = [];
const xxPrun02 = [];
function pmv(a, c) {
const b = cmv[c][~~(a / 24)];
return 24 * ~~(b / 384) + pmul[a % 24][(b >> 4) % 24];
}
function fmv(b, c) {
const a = cmv[c][b >> 4];
return (~~(a / 384) << 4) | (fmul[b & 15][(a >> 4) % 24] ^ (a & 15));
}
function i2f(a, c) {
for (let b = 3; 0 <= b; b--) (c[b] = a & 1), (a >>= 1);
}
function f2i(c) {
for (var a = 0, b = 0; 4 > b; b++) (a <<= 1), (a |= c[b]);
return a;
}
function fullmv(idx, move) {
const slice = cmv[move][~~(idx / 384)];
const flip = fmul[idx & 15][(slice >> 4) % 24] ^ (slice & 15);
const perm = pmul[(idx >> 4) % 24][(slice >> 4) % 24];
return ~~(slice / 384) * 384 + 16 * perm + flip;
}
let isInit = false;
function init() {
if (isInit) return;
isInit = true;
for (var i = 0; i < 24; i++) {
pmul[i] = [];
}
for (var i = 0; i < 16; i++) {
fmul[i] = [];
}
const pm1 = [];
const pm2 = [];
const pm3 = [];
for (var i = 0; i < 24; i++) {
for (let j = 0; j < 24; j++) {
setNPerm(pm1, i, 4);
setNPerm(pm2, j, 4);
for (var k = 0; k < 4; k++) {
pm3[k] = pm1[pm2[k]];
}
pmul[i][j] = getNPerm(pm3, 4);
if (i < 16) {
i2f(i, pm1);
for (var k = 0; k < 4; k++) {
pm3[k] = pm1[pm2[k]];
}
fmul[i][j] = f2i(pm3);
}
}
}
createMove(cmv, 495, getmv);
permPrun = [];
flipPrun = [];
createPrun(permPrun, 0, 11880, 5, pmv);
createPrun(flipPrun, 0, 7920, 6, fmv);
//combMove[comb][m] = comb*, flip*, perm*
//newcomb = comb*, newperm = perm x perm*, newflip = flip x perm* ^ flip*
function getmv(comb, m) {
const arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let r = 4;
for (var i = 0; i < 12; i++) {
if (comb >= Cnk[11 - i][r]) {
comb -= Cnk[11 - i][r--];
arr[i] = r << 1;
} else {
arr[i] = -1;
}
}
edgeMove(arr, m);
(comb = 0), (r = 4);
let t = 0;
const pm = [];
for (var i = 0; i < 12; i++) {
if (arr[i] >= 0) {
comb += Cnk[11 - i][r--];
pm[r] = arr[i] >> 1;
t |= (arr[i] & 1) << (3 - r);
}
}
return ((comb * 24 + getNPerm(pm, 4)) << 4) | t;
}
}
let isxxInit = false;
function xxinit() {
if (isxxInit) return;
isxxInit = true;
xinit();
const obj1 = 4;
let obj2 = 5;
createPrun(
xxPrun01,
obj1 * 3 * 24 + obj1 * 2 + 576 * (obj2 * 3 * 24 + obj2 * 2),
576 * 576,
7,
function (q, m) {
const ec1 = q % 576;
const ec2 = ~~(q / 576);
return (
c1mv[~~(ec1 / 24)][m] * 24 +
e1mv[ec1 % 24][m] +
576 * (c1mv[~~(ec2 / 24)][m] * 24 + e1mv[ec2 % 24][m])
);
}
);
obj2 = 6;
createPrun(
xxPrun02,
obj1 * 3 * 24 + obj1 * 2 + 576 * (obj2 * 3 * 24 + obj2 * 2),
576 * 576,
7,
function (q, m) {
const ec1 = q % 576;
const ec2 = ~~(q / 576);
return (
c1mv[~~(ec1 / 24)][m] * 24 +
e1mv[ec1 % 24][m] +
576 * (c1mv[~~(ec2 / 24)][m] * 24 + e1mv[ec2 % 24][m])
);
}
);
}
let isxInit = false;
function xinit() {
if (isxInit) return;
isxInit = true;
init();
for (let i = 0; i < 24; i++) {
c1mv[i] = [];
e1mv[i] = [];
for (let m = 0; m < 6; m++) {
c1mv[i][m] = cornMove(i, m);
const edge = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];
edge[i >> 1] = i & 1;
edgeMove(edge, m);
for (let e = 0; e < 12; e++) {
if (edge[e] >= 0) {
e1mv[i][m] = (e << 1) | edge[e];
break;
}
}
}
}
ecPrun = [];
for (let obj = 0; obj < 4; obj++) {
const prun = [];
createPrun(prun, (obj + 4) * 3 * 24 + (obj + 4) * 2, 576, 5, function (q, m) {
return c1mv[~~(q / 24)][m] * 24 + e1mv[q % 24][m];
});
ecPrun[obj] = prun;
}
function cornMove(corn, m) {
const idx = ~~(corn / 3);
let twst = corn % 3;
const idxt = [
[3, 1, 2, 7, 0, 5, 6, 4],
[0, 1, 6, 2, 4, 5, 7, 3],
[1, 2, 3, 0, 4, 5, 6, 7],
[0, 5, 1, 3, 4, 6, 2, 7],
[4, 0, 2, 3, 5, 1, 6, 7],
[0, 1, 2, 3, 7, 4, 5, 6],
];
const twstt = [
[2, 0, 0, 1, 1, 0, 0, 2],
[0, 0, 1, 2, 0, 0, 2, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 2, 0, 0, 2, 1, 0],
[1, 2, 0, 0, 2, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
twst = (twst + twstt[m][idx]) % 3;
return idxt[m][idx] * 3 + twst;
}
}
//e4perm, e4flip, e1, c1
//obj: -1:only cross.
// i-4: end when e==i*2, c==i*3
function idaxxcross(q, t, e, c, xxPrun, l, lm, sol) {
if (l == 0) {
return (
q == 0 &&
t == 0 &&
e[0] == 4 * 2 &&
c[0] == 4 * 3 &&
((e[1] == 5 * 2 && c[1] == 5 * 3) || (e[1] == 6 * 2 && c[1] == 6 * 3))
);
} else {
if (
getPruning(permPrun, q) > l ||
getPruning(flipPrun, t) > l ||
getPruning(xxPrun, c[0] * 24 + e[0] + 576 * (c[1] * 24 + e[1])) > l
)
return false;
let p, s, ex, cx, a, m;
for (m = 0; m < 6; m++) {
if (m != lm && m != lm - 3) {
p = q;
s = t;
ex = e;
cx = c;
for (a = 0; a < 3; a++) {
p = pmv(p, m);
s = fmv(s, m);
ex = [e1mv[ex[0]][m], e1mv[ex[1]][m]];
cx = [c1mv[cx[0]][m], c1mv[cx[1]][m]];
if (idaxxcross(p, s, ex, cx, xxPrun, l - 1, m, sol)) {
sol.push("FRUBLD".charAt(m) + " 2'".charAt(a));
return true;
}
}
}
}
}
return false;
}
//e4perm, e4flip, e1, c1
//obj: -1:only cross.
// i-4: end when e==i*2, c==i*3
function idaxcross(q, t, e, c, obj, l, lm, sol) {
if (l == 0) {
return q == 0 && t == 0 && e == (obj + 4) * 2 && c == (obj + 4) * 3;
} else {
if (
getPruning(permPrun, q) > l ||
getPruning(flipPrun, t) > l ||
getPruning(ecPrun[obj], c * 24 + e) > l
)
return false;
let p, s, ex, cx, a, m;
for (m = 0; m < 6; m++) {
if (m != lm && m != lm - 3) {
p = q;
s = t;
ex = e;
cx = c;
for (a = 0; a < 3; a++) {
p = pmv(p, m);
s = fmv(s, m);
ex = e1mv[ex][m];
cx = c1mv[cx][m];
if (idaxcross(p, s, ex, cx, obj, l - 1, m, sol)) {
sol.push("FRUBLD".charAt(m) + " 2'".charAt(a));
return true;
}
}
}
}
}
return false;
}
//e4perm, e4flip
function idacross(q, t, l, lm, sol) {
if (l == 0) {
return q == 0 && t == 0;
} else {
if (getPruning(permPrun, q) > l || getPruning(flipPrun, t) > l) return false;
let p, s, a, m;
for (m = 0; m < 6; m++) {
if (m != lm && m != lm - 3) {
p = q;
s = t;
for (a = 0; a < 3; a++) {
p = pmv(p, m);
s = fmv(s, m);
if (idacross(p, s, l - 1, m, sol)) {
sol.push("FRUBLD".charAt(m) + " 2'".charAt(a));
return true;
}
}
}
}
}
return false;
}
const faceStr = ["D", "U", "L", "R", "F", "B"];
const moveIdx = ["FRUBLD", "FLDBRU", "FDRBUL", "FULBDR", "URBDLF", "DRFULB"];
const rotIdx = [" ", "z2", "z'", "z ", "x'", "x "];
const yrotIdx = ["FRUBLD", "RBULFD", "BLUFRD", "LFURBD"];
let curScramble;
export function solve_cross(moves: string) {
init();
const ret = [];
for (let face = 0; face < 6; face++) {
let flip = 0;
let perm = 0;
for (let i = 0; i < moves.length; i++) {
const m = moveIdx[face].indexOf("FRUBLD".charAt(moves[i][0]));
const p = moves[i][2];
for (let j = 0; j < p; j++) {
flip = fmv(flip, m);
perm = pmv(perm, m);
}
}
const sol = [];
for (let len = 0; len < 100; len++) {
if (idacross(perm, flip, len, -1, sol)) {
break;
}
}
sol.reverse();
ret.push(sol);
}
return ret;
}
function solve_xxcross(moves, face) {
xxinit();
const states = [];
let yrot = 0;
for (yrot = 0; yrot < 4; yrot++) {
let flip = 0;
let perm = 0;
let e1 = [8, 10, 12];
let c1 = [12, 15, 18];
for (var i = 0; i < moves.length; i++) {
const m = yrotIdx[yrot].indexOf(
"FRUBLD".charAt(moveIdx[face].indexOf("FRUBLD".charAt(moves[i][0])))
);
const p = moves[i][2];
for (let j = 0; j < p; j++) {
flip = fmv(flip, m);
perm = pmv(perm, m);
e1 = [e1mv[e1[0]][m], e1mv[e1[1]][m], e1mv[e1[2]][m]];
c1 = [c1mv[c1[0]][m], c1mv[c1[1]][m], c1mv[c1[2]][m]];
}
}
states.push([perm, flip, e1, c1]);
}
const sol = [];
let found = false;
let len = 0;
while (!found) {
for (yrot = 0; yrot < 4; yrot++) {
const state = states[yrot];
if (
idaxxcross(
state[0],
state[1],
[state[2][0], state[2][1]],
[state[3][0], state[3][1]],
xxPrun01,
len,
-1,
sol
)
) {
found = true;
break;
}
if (
idaxxcross(
state[0],
state[1],
[state[2][0], state[2][2]],
[state[3][0], state[3][2]],
xxPrun02,
len,
-1,
sol
)
) {
found = true;
break;
}
}
len++;
}
sol.reverse();
for (var i = 0; i < sol.length; i++) {
sol[i] = yrotIdx[yrot]["FRUBLD".indexOf(sol[i][0])] + sol[i][1];
}
return sol;
}
function solve_xcross(moves, face) {
xinit();
let flip = 0;
let perm = 0;
const e1 = [8, 10, 12, 14];
const c1 = [12, 15, 18, 21];
for (let i = 0; i < moves.length; i++) {
const m = moveIdx[face].indexOf("FRUBLD".charAt(moves[i][0]));
const p = moves[i][2];
for (let j = 0; j < p; j++) {
flip = fmv(flip, m);
perm = pmv(perm, m);
for (var obj = 0; obj < 4; obj++) {
e1[obj] = e1mv[e1[obj]][m];
c1[obj] = c1mv[c1[obj]][m];
}
}
}
const sol = [];
let found = false;
let len = 0;
while (!found) {
for (var obj = 0; obj < 4; obj++) {
if (idaxcross(perm, flip, e1[obj], c1[obj], obj, len, -1, sol)) {
found = true;
break;
}
}
len++;
}
sol.reverse();
return sol;
}
let isFullInit = false;
function fullInit() {
if (isFullInit) return;
isFullInit = true;
init();
fullPrun = [];
createPrun(fullPrun, 0, 190080, 7, fullmv, 6, 3, 6);
}
function mapCross(idx) {
let comb = ~~(idx / 384);
const perm = (idx >> 4) % 24;
const flip = idx & 15;
const arrp = [];
const arrf = [];
const pm = [];
const fl = [];
i2f(flip, fl);
setNPerm(pm, perm, 4);
let r = 4;
const map = [7, 6, 5, 4, 10, 9, 8, 11, 3, 2, 1, 0];
for (let i = 0; i < 12; i++) {
if (comb >= Cnk[11 - i][r]) {
comb -= Cnk[11 - i][r--];
arrp[map[i]] = pm[r];
arrf[map[i]] = fl[r];
} else {
arrp[map[i]] = arrf[map[i]] = -1;
}
}
return [arrp, arrf];
}
export function getEasyCross(length: number) {
fullInit();
const lenA = Math.min(length % 10, 8);
const lenB = Math.min(~~(length / 10), 8);
const minLen = Math.min(lenA, lenB);
const maxLen = Math.max(lenA, lenB);
const ncase = [0, 1, 16, 174, 1568, 11377, 57758, 155012, 189978, 190080];
let cases = rn(ncase[maxLen + 1] - ncase[minLen]) + 1;
let i;
for (i = 0; i < 190080; i++) {
const prun = getPruning(fullPrun, i);
if (prun <= maxLen && prun >= minLen && --cases == 0) {
break;
}
}
return mapCross(i);
}
export function getEasyXCross(length: number) {
fullInit();
xinit();
const ncase = [1, 16, 174, 1568, 11377, 57758, 155012, 189978, 190080];
length = Math.max(0, Math.min(length, 8));
const remain = ncase[length];
let isFound = false;
const testCnt = 0;
while (!isFound) {
let rndIdx = [];
const sample = 500;
for (var i = 0; i < sample; i++) {
rndIdx.push(rn(remain));
}
rndIdx.sort(function (a, b) {
return b - a;
});
const rndCases = [];
let cnt = 0;
for (var i = 0; i < 190080; i++) {
const prun = getPruning(fullPrun, i);
if (prun > length) {
continue;
}
while (rndIdx[rndIdx.length - 1] == cnt) {
rndCases.push(i);
rndIdx.pop();
}
if (rndIdx.length == 0) {
break;
}
cnt++;
}
rndIdx = rndPerm(sample);
for (var i = 0; i < sample; i++) {
const caze = rndCases[rndIdx[i]];
let comb = ~~(caze / 384);
const perm = comb * 24 + ((caze >> 4) % 24);
const flip = (comb << 4) | (caze & 15);
var sol = [];
const ret = idacross(perm, flip, length, -1, sol);
const corns = rndPerm(8).slice(4);
const edges = rndPerm(8);
const arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let r = 4;
for (var j = 0; j < 12; j++) {
if (comb >= Cnk[11 - j][r]) {
comb -= Cnk[11 - j][r--];
arr[j] = -1;
} else {
arr[j] = edges.pop();
}
}
for (var j = 0; j < 4; j++) {
corns[j] = corns[j] * 3 + rn(3);
edges[j] = arr.indexOf(j) * 2 + rn(2);
if (isFound || getPruning(ecPrun[j], corns[j] * 24 + edges[j]) > length) {
continue;
}
var sol = [];
for (let depth = 0; depth <= length; depth++) {
if (idaxcross(perm, flip, edges[j], corns[j], j, depth, -1, sol)) {
isFound = true;
break;
}
}
}
if (!isFound) {
continue;
}
const crossArr = mapCross(caze);
crossArr[2] = valuedArray(8, -1);
crossArr[3] = valuedArray(8, -1);
const map = [7, 6, 5, 4, 10, 9, 8, 11, 3, 2, 1, 0];
const map2 = [6, 5, 4, 7, 2, 1, 0, 3];
for (var i = 0; i < 4; i++) {
crossArr[0][map[edges[i] >> 1]] = map[i + 4];
crossArr[1][map[edges[i] >> 1]] = edges[i] % 2;
crossArr[2][map2[~~(corns[i] / 3)]] = map2[i + 4];
crossArr[3][map2[~~(corns[i] / 3)]] = (30 - corns[i]) % 3;
}
return crossArr;
}
}
}