/**
* 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, fact } from "./mathlib";
// COMMON FUNCTIONS
const SYM_E2C_MAGIC = 0x00dddd00;
const USE_TWIST_FLIP_PRUN = true;
const USE_CONJ_PRUN = USE_TWIST_FLIP_PRUN;
const SymCube: CubieCube[] = [];
const SymMult: number[][] = [];
const SymMultInv: number[][] = [];
const FlipR2S: number[] = [];
const TwistR2S: number[] = [];
const EPermR2S: number[] = [];
const PermInvEdgeSym: number[] = [];
const SymMove: number[][] = [];
const Sym8Move: number[] = [];
const SymMoveUD: number[][] = [];
const moveCube: CubieCube[] = [];
const FlipS2R: number[] = [];
const Perm2CombP: number[] = [];
const TwistS2R: number[] = [];
const EPermS2R: number[] = [];
const SymStateFlip: number[] = [];
const SymStateTwist: number[] = [];
const SymStatePerm: number[] = [];
const FlipS2RF: number[] = [];
const FlipMove: number[][] = [];
const UDSliceMove: number[][] = [];
const TwistMove: number[][] = [];
const UDSliceConj: number[][] = [];
const UDSliceTwistPrun: number[] = [];
const UDSliceFlipPrun: number[] = [];
const TwistFlipPrun: number[] = [];
function setVal(val0: number, val: number, isEdge: boolean) {
return isEdge ? (val << 1) | (val0 & 1) : val | (val0 & 0xf8);
}
function getVal(val0: number, isEdge: boolean) {
return isEdge ? val0 >> 1 : val0 & 7;
}
function setPruning(table: number[], index: number, value: number) {
table[index >> 3] ^= value << (index << 2); // index << 2 <=> (index & 7) << 2
}
function getPruning(table: number[], index: number) {
return (table[index >> 3] >> (index << 2)) & 0xf; // index << 2 <=> (index & 7) << 2
}
function getPruningMax(maxValue: number, table: number[], index: number) {
return Math.min(maxValue, (table[index >> 3] >> (index << 2)) & 0xf);
}
function hasZero(val: number) {
return ((val - 0x11111111) & ~val & 0x88888888) != 0;
}
function setNPerm(arr: number[], idx: number, n: number, isEdge: boolean) {
n--;
let val = 0x76543210;
for (let i = 0; i < n; ++i) {
const p = fact[n - i];
let v = ~~(idx / p);
idx %= p;
v <<= 2;
arr[i] = setVal(arr[i], (val >> v) & 0xf, isEdge);
const m = (1 << v) - 1;
val = (val & m) + ((val >> 4) & ~m);
}
arr[n] = setVal(arr[n], val & 0xf, isEdge);
}
function getNPerm(arr: number[], n: number, isEdge: boolean) {
let idx = 0,
val = 0x76543210;
for (let i = 0; i < n - 1; ++i) {
const v = getVal(arr[i], isEdge) << 2;
idx = (n - i) * idx + ((val >> v) & 0xf);
val -= 0x11111110 << v;
}
return idx;
}
function setNPermFull(arr: number[], idx: number, n: number, isEdge: boolean) {
arr[n - 1] = setVal(arr[n - 1], 0, isEdge);
for (let i = n - 2; i >= 0; --i) {
arr[i] = setVal(arr[i], idx % (n - i), isEdge);
idx = ~~(idx / (n - i));
for (let j = i + 1; j < n; ++j) {
if (getVal(arr[j], isEdge) >= getVal(arr[i], isEdge)) {
arr[j] = setVal(arr[j], getVal(arr[j], isEdge) + 1, isEdge);
}
}
}
}
function getNPermFull(arr: number[], n: number, isEdge: boolean) {
let idx = 0;
for (let i = 0; i < n; ++i) {
idx *= n - i;
for (let j = i + 1; j < n; ++j) {
if (getVal(arr[j], isEdge) < getVal(arr[i], isEdge)) {
++idx;
}
}
}
return idx;
}
function setComb(arr: number[], idxC: number, mask: number, isEdge: boolean) {
const end = arr.length - 1;
let r = 4,
fill = end;
for (let i = end; i >= 0; i--) {
if (idxC >= Cnk[i][r]) {
idxC -= Cnk[i][r--];
arr[i] = setVal(arr[i], r | mask, isEdge);
} else {
if ((fill & 0xc) == mask) {
fill -= 4;
}
arr[i] = setVal(arr[i], fill--, isEdge);
}
}
}
function getComb(arr: number[], mask: number, isEdge: boolean) {
const end = arr.length - 1;
let idxC = 0;
let r = 4;
for (let i = end; i >= 0; i--) {
const perm = getVal(arr[i], isEdge);
if ((perm & 0xc) == mask) {
idxC += Cnk[i][r--];
}
}
return idxC;
}
function getNParity(idx: number, n: number) {
let p = 0;
for (let i = n - 2; i >= 0; i--) {
p ^= idx % (n - i);
idx = ~~(idx / (n - i));
}
return p & 1;
}
function ESym2CSym(idx: number) {
return idx ^ ((SYM_E2C_MAGIC >> ((idx & 0xf) << 1)) & 3);
}
function initRawSymPrun(
PrunTable: number[],
N_RAW: number,
N_SYM: number,
RawMove: number[][] | null,
RawConj: number[][] | null,
SymMove: number[][],
SymState: number[],
PrunFlag: number
) {
const SYM_SHIFT = PrunFlag & 0xf;
const SYM_E2C_MAGIC = ((PrunFlag >> 4) & 1) == 1 ? 0x00dddd00 : 0x00000000;
const IS_PHASE2 = ((PrunFlag >> 5) & 1) == 1;
const INV_DEPTH = (PrunFlag >> 8) & 0xf;
const MAX_DEPTH = (PrunFlag >> 12) & 0xf;
const MIN_DEPTH = (PrunFlag >> 16) & 0xf;
const SYM_MASK = (1 << SYM_SHIFT) - 1;
const ISTFP = RawMove == null;
const N_SIZE = N_RAW * N_SYM;
const N_MOVES = IS_PHASE2 ? 10 : 18;
const NEXT_AXIS_MAGIC = N_MOVES == 10 ? 0x42 : 0x92492;
let depth = getPruning(PrunTable, N_SIZE) - 1;
if (depth == -1) {
for (let i = 0; i < (N_SIZE >> 3) + 1; i++) {
PrunTable[i] = 0xffffffff;
}
setPruning(PrunTable, 0, 0 ^ 0xf);
depth = 0;
} else {
setPruning(PrunTable, N_SIZE, 0xf ^ (depth + 1));
}
const SEARCH_DEPTH =
PARTIAL_INIT_LEVEL > 0 ? Math.min(Math.max(depth + 1, MIN_DEPTH), MAX_DEPTH) : MAX_DEPTH;
while (depth < SEARCH_DEPTH) {
const inv = depth > INV_DEPTH;
const select = inv ? 0xf : depth;
const selArrMask = select * 0x11111111;
const check = inv ? depth : 0xf;
depth++;
InitPrunProgress++;
const xorVal = depth ^ 0xf;
let done = 0;
let val = 0;
for (let i = 0; i < N_SIZE; i++, val >>= 4) {
if ((i & 7) == 0) {
val = PrunTable[i >> 3];
if (!hasZero(val ^ selArrMask)) {
i += 7;
continue;
}
}
if ((val & 0xf) != select) {
continue;
}
const raw = i % N_RAW;
const sym = ~~(i / N_RAW);
let flip = 0,
fsym = 0;
if (ISTFP) {
flip = FlipR2S[raw];
fsym = flip & 7;
flip >>= 3;
}
for (let m = 0; m < N_MOVES; m++) {
let symx = SymMove[sym][m];
let rawx;
if (ISTFP) {
rawx = FlipS2RF[FlipMove[flip][Sym8Move[(m << 3) | fsym]] ^ fsym ^ (symx & SYM_MASK)];
} else {
rawx = RawConj![RawMove![raw][m]][symx & SYM_MASK];
}
symx >>= SYM_SHIFT;
const idx = symx * N_RAW + rawx;
const prun = getPruning(PrunTable, idx);
if (prun != check) {
if (prun < depth - 1) {
m += (NEXT_AXIS_MAGIC >> m) & 3;
}
continue;
}
done++;
if (inv) {
setPruning(PrunTable, i, xorVal);
break;
}
setPruning(PrunTable, idx, xorVal);
for (let j = 1, symState = SymState[symx]; (symState >>= 1) != 0; j++) {
if ((symState & 1) != 1) {
continue;
}
let idxx = symx * N_RAW;
if (ISTFP) {
idxx += FlipS2RF[FlipR2S[rawx] ^ j];
} else {
idxx += RawConj![rawx][j ^ ((SYM_E2C_MAGIC >> (j << 1)) & 3)];
}
if (getPruning(PrunTable, idxx) == check) {
setPruning(PrunTable, idxx, xorVal);
done++;
}
}
}
}
}
setPruning(PrunTable, N_SIZE, (depth + 1) ^ 0xf);
return depth + 1;
}
//--------------------------------------------
const cornerFacelet = [
[8, 9, 20],
[6, 18, 38],
[0, 36, 47],
[2, 45, 11],
[29, 26, 15],
[27, 44, 24],
[33, 53, 42],
[35, 17, 51],
];
const edgeFacelet = [
[5, 10],
[7, 19],
[3, 37],
[1, 46],
[32, 16],
[28, 25],
[30, 43],
[34, 52],
[23, 12],
[21, 41],
[50, 39],
[48, 14],
];
class CubieCube {
ca = [0, 1, 2, 3, 4, 5, 6, 7];
ea = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22];
static urf1 = new CubieCube().initCoord(2531, 1373, 67026819, 1367);
static urf2 = new CubieCube().initCoord(2089, 1906, 322752913, 2040);
static EdgeMult(a: CubieCube, b: CubieCube, prod: CubieCube) {
for (let ed = 0; ed < 12; ed += 1) {
prod.ea[ed] = a.ea[b.ea[ed] >> 1] ^ (b.ea[ed] & 1);
}
}
static CornMult(a: CubieCube, b: CubieCube, prod: CubieCube) {
for (let corn = 0; corn < 8; corn += 1) {
const ori = ((a.ca[b.ca[corn] & 7] >> 3) + (b.ca[corn] >> 3)) % 3;
prod.ca[corn] = (a.ca[b.ca[corn] & 7] & 7) | (ori << 3);
}
}
static CornMultFull(a: CubieCube, b: CubieCube, prod: CubieCube) {
for (let corn = 0; corn < 8; corn += 1) {
const oriA = a.ca[b.ca[corn] & 7] >> 3;
const oriB = b.ca[corn] >> 3;
let ori = oriA + (oriA < 3 ? oriB : 6 - oriB);
ori = (ori % 3) + (oriA < 3 == oriB < 3 ? 0 : 3);
prod.ca[corn] = (a.ca[b.ca[corn] & 7] & 7) | (ori << 3);
}
}
static CornConjugate(a: CubieCube, idx: number, b: CubieCube) {
const sinv = SymCube[SymMultInv[0][idx]];
const s = SymCube[idx];
for (let corn = 0; corn < 8; corn += 1) {
const oriA = sinv.ca[a.ca[s.ca[corn] & 7] & 7] >> 3;
const oriB = a.ca[s.ca[corn] & 7] >> 3;
const ori = oriA < 3 ? oriB : (3 - oriB) % 3;
b.ca[corn] = (sinv.ca[a.ca[s.ca[corn] & 7] & 7] & 7) | (ori << 3);
}
}
static EdgeConjugate(a: CubieCube, idx: number, b: CubieCube) {
const sinv = SymCube[SymMultInv[0][idx]];
const s = SymCube[idx];
for (let ed = 0; ed < 12; ed++) {
b.ea[ed] = sinv.ea[a.ea[s.ea[ed] >> 1] >> 1] ^ (a.ea[s.ea[ed] >> 1] & 1) ^ (s.ea[ed] & 1);
}
}
init(ca: number[], ea: number[]): CubieCube {
this.ca = ca.slice();
this.ea = ea.slice();
return this;
}
initCoord(cperm: number, twist: number, eperm: number, flip: number): CubieCube {
setNPerm(this.ca, cperm, 8, false);
this.setTwist(twist);
setNPermFull(this.ea, eperm, 12, true);
this.setFlip(flip);
return this;
}
isEqual(c: CubieCube): boolean {
if (this.ca.some((v, i) => v != c.ca[i]) || this.ea.some((v, i) => v != c.ea[i])) {
return false;
}
return true;
}
setFlip(idx: number) {
let parity = 0;
let val: number;
for (let i = 10; i >= 0; i--, idx >>= 1) {
parity ^= val = idx & 1;
this.ea[i] = (this.ea[i] & 0xfe) | val;
}
this.ea[11] = (this.ea[11] & 0xfe) | parity;
}
getFlip() {
let idx = 0;
for (let i = 0; i < 11; i++) {
idx = (idx << 1) | (this.ea[i] & 1);
}
return idx;
}
getFlipSym() {
return FlipR2S[this.getFlip()];
}
setTwist(idx: number) {
let twst = 15;
let val;
for (let i = 6; i >= 0; i--, idx = ~~(idx / 3)) {
twst -= val = idx % 3;
this.ca[i] = (this.ca[i] & 0x7) | (val << 3);
}
this.ca[7] = (this.ca[7] & 0x7) | (twst % 3 << 3);
}
getTwist() {
let idx = 0;
for (let i = 0; i < 7; i++) {
idx += (idx << 1) + (this.ca[i] >> 3);
}
return idx;
}
getTwistSym() {
return TwistR2S[this.getTwist()];
}
setCPerm(idx: number) {
setNPerm(this.ca, idx, 8, false);
}
getCPerm() {
return getNPerm(this.ca, 8, false);
}
getCPermSym() {
return ESym2CSym(EPermR2S[getNPerm(this.ca, 8, false)]);
}
setEPerm(idx: number) {
setNPerm(this.ea, idx, 8, true);
}
getEPerm() {
return getNPerm(this.ea, 8, true);
}
getEPermSym() {
return EPermR2S[getNPerm(this.ea, 8, true)];
}
getUDSlice() {
return 494 - getComb(this.ea, 8, true);
}
setUDSlice(idx: number) {
setComb(this.ea, 494 - idx, 8, true);
}
getMPerm() {
return getNPermFull(this.ea, 12, true) % 24;
}
setMPerm(idx: number) {
setNPermFull(this.ea, idx, 12, true);
}
getCComb() {
return getComb(this.ca, 0, false);
}
setCComb(idx: number) {
setComb(this.ca, idx, 0, false);
}
URFConjugate() {
const temps = new CubieCube();
CubieCube.CornMult(CubieCube.urf2, this, temps);
CubieCube.CornMult(temps, CubieCube.urf1, this);
CubieCube.EdgeMult(CubieCube.urf2, this, temps);
CubieCube.EdgeMult(temps, CubieCube.urf1, this);
}
toFaceCube(cFacelet: number[][] = cornerFacelet, eFacelet: number[][] = edgeFacelet) {
const ts = "URFDLB";
const f: string[] = [];
for (let i = 0; i < 54; i++) {
f[i] = ts[~~(i / 9)];
}
for (let c = 0; c < 8; c++) {
const j = this.ca[c] & 0x7; // cornercubie with index j is at
const ori = this.ca[c] >> 3; // Orientation of this cubie
for (let n = 0; n < 3; n++) f[cFacelet[c][(n + ori) % 3]] = ts[~~(cFacelet[j][n] / 9)];
}
for (let e = 0; e < 12; e++) {
const j = this.ea[e] >> 1; // edgecubie with index j is at edgeposition
const ori = this.ea[e] & 1; // Orientation of this cubie
for (let n = 0; n < 2; n++) f[eFacelet[e][(n + ori) % 2]] = ts[~~(eFacelet[j][n] / 9)];
}
return f.join("");
}
invFrom(cc: CubieCube): CubieCube {
for (let edge = 0; edge < 12; edge++) {
this.ea[cc.ea[edge] >> 1] = (edge << 1) | (cc.ea[edge] & 1);
}
for (let corn = 0; corn < 8; corn++) {
this.ca[cc.ca[corn] & 0x7] = corn | ((0x20 >> (cc.ca[corn] >> 3)) & 0x18);
}
return this;
}
fromFacelet(
facelet: string,
cFacelet: number[][] = cornerFacelet,
eFacelet: number[][] = edgeFacelet
) {
let count = 0;
const f = [];
const centers =
facelet[4] + facelet[13] + facelet[22] + facelet[31] + facelet[40] + facelet[49];
for (let i = 0; i < 54; ++i) {
f[i] = centers.indexOf(facelet[i]);
if (f[i] == -1) {
return -1;
}
count += 1 << (f[i] << 2);
}
if (count != 0x999999) {
return -1;
}
let col1, col2, i, j, ori;
for (i = 0; i < 8; ++i) {
for (ori = 0; ori < 3; ++ori) if (f[cFacelet[i][ori]] == 0 || f[cFacelet[i][ori]] == 3) break;
col1 = f[cFacelet[i][(ori + 1) % 3]];
col2 = f[cFacelet[i][(ori + 2) % 3]];
for (j = 0; j < 8; ++j) {
if (col1 == ~~(cFacelet[j][1] / 9) && col2 == ~~(cFacelet[j][2] / 9)) {
this.ca[i] = j | (ori % 3 << 3);
break;
}
}
}
for (i = 0; i < 12; ++i) {
for (j = 0; j < 12; ++j) {
if (
f[eFacelet[i][0]] == ~~(eFacelet[j][0] / 9) &&
f[eFacelet[i][1]] == ~~(eFacelet[j][1] / 9)
) {
this.ea[i] = j << 1;
break;
}
if (
f[eFacelet[i][0]] == ~~(eFacelet[j][1] / 9) &&
f[eFacelet[i][1]] == ~~(eFacelet[j][0] / 9)
) {
this.ea[i] = (j << 1) | 1;
break;
}
}
}
}
}
//--------------------------------------------
class CoordCube {
twist = 0;
tsym = 0;
flip = 0;
fsym = 0;
slice = 0;
prun = 0;
twistc = 0;
flipc = 0;
static UDSliceTwistPrunMax = 15;
set(node: CoordCube) {
this.twist = node.twist;
this.tsym = node.tsym;
this.flip = node.flip;
this.fsym = node.fsym;
this.slice = node.slice;
this.prun = node.prun;
if (USE_CONJ_PRUN) {
this.twistc = node.twistc;
this.flipc = node.flipc;
}
}
calcPruning() {
this.prun = Math.max(
Math.max(
getPruningMax(
CoordCube.UDSliceTwistPrunMax,
UDSliceTwistPrun,
this.twist * N_SLICE + UDSliceConj[this.slice][this.tsym]
),
getPruningMax(
UDSliceFlipPrunMax,
UDSliceFlipPrun,
this.flip * N_SLICE + UDSliceConj[this.slice][this.fsym]
)
),
Math.max(
USE_CONJ_PRUN
? getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
((this.twistc >> 3) << 11) | FlipS2RF[this.flipc ^ (this.twistc & 7)]
)
: 0,
USE_TWIST_FLIP_PRUN
? getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
(this.twist << 11) | FlipS2RF[(this.flip << 3) | (this.fsym ^ this.tsym)]
)
: 0
)
);
}
setWithPrun(cc: CubieCube, depth: number) {
this.twist = cc.getTwistSym();
this.flip = cc.getFlipSym();
this.tsym = this.twist & 7;
this.twist = this.twist >> 3;
this.prun = USE_TWIST_FLIP_PRUN
? getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
(this.twist << 11) | FlipS2RF[this.flip ^ this.tsym]
)
: 0;
if (this.prun > depth) {
return false;
}
this.fsym = this.flip & 7;
this.flip = this.flip >> 3;
this.slice = cc.getUDSlice();
this.prun = Math.max(
this.prun,
Math.max(
getPruningMax(
CoordCube.UDSliceTwistPrunMax,
UDSliceTwistPrun,
this.twist * N_SLICE + UDSliceConj[this.slice][this.tsym]
),
getPruningMax(
UDSliceFlipPrunMax,
UDSliceFlipPrun,
this.flip * N_SLICE + UDSliceConj[this.slice][this.fsym]
)
)
);
if (this.prun > depth) {
return false;
}
if (USE_CONJ_PRUN) {
const pc = new CubieCube();
CubieCube.CornConjugate(cc, 1, pc);
CubieCube.EdgeConjugate(cc, 1, pc);
this.twistc = pc.getTwistSym();
this.flipc = pc.getFlipSym();
this.prun = Math.max(
this.prun,
getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
((this.twistc >> 3) << 11) | FlipS2RF[this.flipc ^ (this.twistc & 7)]
)
);
}
return this.prun <= depth;
}
doMovePrun(cc: CoordCube, m: number) {
this.slice = UDSliceMove[cc.slice][m];
this.flip = FlipMove[cc.flip][Sym8Move[(m << 3) | cc.fsym]];
this.fsym = (this.flip & 7) ^ cc.fsym;
this.flip >>= 3;
this.twist = TwistMove[cc.twist][Sym8Move[(m << 3) | cc.tsym]];
this.tsym = (this.twist & 7) ^ cc.tsym;
this.twist >>= 3;
this.prun = Math.max(
Math.max(
getPruningMax(
CoordCube.UDSliceTwistPrunMax,
UDSliceTwistPrun,
this.twist * N_SLICE + UDSliceConj[this.slice][this.tsym]
),
getPruningMax(
UDSliceFlipPrunMax,
UDSliceFlipPrun,
this.flip * N_SLICE + UDSliceConj[this.slice][this.fsym]
)
),
USE_TWIST_FLIP_PRUN
? getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
(this.twist << 11) | FlipS2RF[(this.flip << 3) | (this.fsym ^ this.tsym)]
)
: 0
);
return this.prun;
}
doMovePrunConj(cc: CoordCube, m: number) {
m = SymMove[3][m];
this.flipc = FlipMove[cc.flipc >> 3][Sym8Move[(m << 3) | (cc.flipc & 7)]] ^ (cc.flipc & 7);
this.twistc = TwistMove[cc.twistc >> 3][Sym8Move[(m << 3) | (cc.twistc & 7)]] ^ (cc.twistc & 7);
return getPruningMax(
TwistFlipPrunMax,
TwistFlipPrun,
((this.twistc >> 3) << 11) | FlipS2RF[this.flipc ^ (this.twistc & 7)]
);
}
}
//--------------------------------------------
const MAX_PRE_MOVES = 20;
const TRY_INVERSE = true;
const TRY_THREE_AXES = true;
const MIN_P1LENGTH_PRE = 7;
const MAX_DEPTH2 = 13;
const INVERSE_SOLUTION = 0x2;
const move2str = [
"U ",
"U2",
"U'",
"R ",
"R2",
"R'",
"F ",
"F2",
"F'",
"D ",
"D2",
"D'",
"L ",
"L2",
"L'",
"B ",
"B2",
"B'",
];
const urfMove = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
[6, 7, 8, 0, 1, 2, 3, 4, 5, 15, 16, 17, 9, 10, 11, 12, 13, 14],
[3, 4, 5, 6, 7, 8, 0, 1, 2, 12, 13, 14, 15, 16, 17, 9, 10, 11],
[2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, 17, 16, 15],
[8, 7, 6, 2, 1, 0, 5, 4, 3, 17, 16, 15, 11, 10, 9, 14, 13, 12],
[5, 4, 3, 8, 7, 6, 2, 1, 0, 14, 13, 12, 17, 16, 15, 11, 10, 9],
];
function getPermSymInv(idx: number, sym: number, isCorner: boolean) {
let idxi = PermInvEdgeSym[idx];
if (isCorner) {
idxi = ESym2CSym(idxi);
}
return (idxi & 0xfff0) | SymMult[idxi & 0xf][sym];
}
export class Search {
move: number[] = [];
moveSol: number[] | null = null;
nodeUD: CoordCube[] = [];
valid1 = 0;
allowShorter = false;
cc = new CubieCube();
urfCubieCube: CubieCube[] = [];
urfCoordCube: CoordCube[] = [];
phase1Cubie: CubieCube[] = [];
preMoveCubes: CubieCube[] = [];
preMoves: number[] = [];
preMoveLen = 0;
maxPreMoves = 0;
sol = 0;
probe = -1;
probeMax = 1e9;
probeMin = 0;
verbose = 0;
conjMask = 0;
length1 = 0;
depth1 = 0;
urfIdx = 0;
isRec = false;
strSolution: string | null = "";
constructor() {
for (let i = 0; i < 21; i++) {
this.nodeUD[i] = new CoordCube();
this.phase1Cubie[i] = new CubieCube();
}
for (let i = 0; i < 6; i++) {
this.urfCubieCube[i] = new CubieCube();
this.urfCoordCube[i] = new CoordCube();
}
for (let i = 0; i < MAX_PRE_MOVES; i++) {
this.preMoveCubes[i + 1] = new CubieCube();
}
}
solution(facelets: string, maxDepth = 21, probeMax = 1e9, probeMin = 0, verbose = 0): string {
initPrunTables();
const check = this.verify(facelets);
if (check != 0) {
return "Error " + Math.abs(check);
}
this.sol = maxDepth + 1;
this.probe = 0;
this.probeMax = probeMax;
this.probeMin = Math.min(probeMin, probeMax);
this.verbose = verbose;
this.moveSol = null;
this.isRec = false;
this.initSearch();
return this.search();
}
initSearch() {
this.conjMask = (TRY_INVERSE ? 0 : 0x38) | (TRY_THREE_AXES ? 0 : 0x36);
this.maxPreMoves = this.conjMask > 7 ? 0 : MAX_PRE_MOVES;
for (let i = 0; i < 6; i++) {
this.urfCubieCube[i].init(this.cc.ca, this.cc.ea);
this.urfCoordCube[i].setWithPrun(this.urfCubieCube[i], 20);
this.cc.URFConjugate();
if (i % 3 == 2) {
const tmp = new CubieCube().invFrom(this.cc);
this.cc.init(tmp.ca, tmp.ea);
}
}
}
next(probeMax: number, probeMin: number, verbose: number) {
this.probe = 0;
this.probeMax = probeMax;
this.probeMin = Math.min(probeMin, probeMax);
this.moveSol = null;
this.isRec = true;
this.verbose = verbose;
return this.search();
}
verify(facelets: string) {
if (this.cc.fromFacelet(facelets) == -1) {
return -1;
}
let sum = 0;
let edgeMask = 0;
for (let e = 0; e < 12; e++) {
edgeMask |= 1 << (this.cc.ea[e] >> 1);
sum ^= this.cc.ea[e] & 1;
}
if (edgeMask != 0xfff) {
return -2; // missing edges
}
if (sum != 0) {
return -3;
}
let cornMask = 0;
sum = 0;
for (let c = 0; c < 8; c++) {
cornMask |= 1 << (this.cc.ca[c] & 7);
sum += this.cc.ca[c] >> 3;
}
if (cornMask != 0xff) {
return -4; // missing corners
}
if (sum % 3 != 0) {
return -4 - (sum % 3); // twisted corner
}
if (
(getNParity(getNPermFull(this.cc.ea, 12, true), 12) ^ getNParity(this.cc.getCPerm(), 8)) !=
0
) {
return -7; // parity error
}
return 0; // cube ok
}
phase1PreMoves(maxl: number, lm: number, cc: CubieCube) {
this.preMoveLen = this.maxPreMoves - maxl;
if (
this.isRec
? this.depth1 == this.length1 - this.preMoveLen
: this.preMoveLen == 0 || ((0x36fb7 >> lm) & 1) == 0
) {
this.depth1 = this.length1 - this.preMoveLen;
this.phase1Cubie[0].init(cc.ca, cc.ea) /* = cc*/;
this.allowShorter = this.depth1 == MIN_P1LENGTH_PRE && this.preMoveLen != 0;
if (
this.nodeUD[this.depth1 + 1].setWithPrun(cc, this.depth1) &&
this.phase1(this.nodeUD[this.depth1 + 1], this.depth1, -1) == 0
) {
return 0;
}
}
if (maxl == 0 || this.preMoveLen + MIN_P1LENGTH_PRE >= this.length1) {
return 1;
}
let skipMoves = 0;
if (maxl == 1 || this.preMoveLen + 1 + MIN_P1LENGTH_PRE >= this.length1) {
//last pre move
skipMoves |= 0x36fb7; // 11 0110 1111 1011 0111
}
lm = ~~(lm / 3) * 3;
for (let m = 0; m < 18; m++) {
if (m == lm || m == lm - 9 || m == lm + 9) {
m += 2;
continue;
}
if (
(this.isRec && m != this.preMoves[this.maxPreMoves - maxl]) ||
(skipMoves & (1 << m)) != 0
) {
continue;
}
CubieCube.CornMult(moveCube[m], cc, this.preMoveCubes[maxl]);
CubieCube.EdgeMult(moveCube[m], cc, this.preMoveCubes[maxl]);
this.preMoves[this.maxPreMoves - maxl] = m;
const ret = this.phase1PreMoves(maxl - 1, m, this.preMoveCubes[maxl]);
if (ret == 0) {
return 0;
}
}
return 1;
}
search(): string {
for (this.length1 = this.isRec ? this.length1 : 0; this.length1 < this.sol; this.length1++) {
for (this.urfIdx = this.isRec ? this.urfIdx : 0; this.urfIdx < 6; this.urfIdx++) {
if ((this.conjMask & (1 << this.urfIdx)) != 0) {
continue;
}
if (this.phase1PreMoves(this.maxPreMoves, -30, this.urfCubieCube[this.urfIdx]) == 0) {
return this.strSolution == null ? "Error 8" : this.strSolution;
}
}
}
return this.strSolution == null ? "Error 7" : this.strSolution;
}
initPhase2Pre() {
this.isRec = false;
if (this.probe >= (this.moveSol == null ? this.probeMax : this.probeMin)) {
return 0;
}
++this.probe;
for (let i = this.valid1; i < this.depth1; i++) {
CubieCube.CornMult(this.phase1Cubie[i], moveCube[this.move[i]], this.phase1Cubie[i + 1]);
CubieCube.EdgeMult(this.phase1Cubie[i], moveCube[this.move[i]], this.phase1Cubie[i + 1]);
}
this.valid1 = this.depth1;
let ret = this.initPhase2(this.phase1Cubie[this.depth1]);
if (ret == 0 || this.preMoveLen == 0 || ret == 2) {
return ret;
}
const m = ~~(this.preMoves[this.preMoveLen - 1] / 3) * 3 + 1;
CubieCube.CornMult(
moveCube[m],
this.phase1Cubie[this.depth1],
this.phase1Cubie[this.depth1 + 1]
);
CubieCube.EdgeMult(
moveCube[m],
this.phase1Cubie[this.depth1],
this.phase1Cubie[this.depth1 + 1]
);
this.preMoves[this.preMoveLen - 1] += 2 - (this.preMoves[this.preMoveLen - 1] % 3) * 2;
ret = this.initPhase2(this.phase1Cubie[this.depth1 + 1]);
this.preMoves[this.preMoveLen - 1] += 2 - (this.preMoves[this.preMoveLen - 1] % 3) * 2;
return ret;
}
initPhase2(phase2Cubie: CubieCube) {
let p2corn = phase2Cubie.getCPermSym();
const p2csym = p2corn & 0xf;
p2corn >>= 4;
let p2edge = phase2Cubie.getEPermSym();
const p2esym = p2edge & 0xf;
p2edge >>= 4;
const p2mid = phase2Cubie.getMPerm();
const prun = Math.max(
getPruningMax(
EPermCCombPPrunMax,
EPermCCombPPrun,
p2edge * N_COMB + CCombPConj[Perm2CombP[p2corn] & 0xff][SymMultInv[p2esym][p2csym]]
),
getPruningMax(MCPermPrunMax, MCPermPrun, p2corn * N_MPERM + MPermConj[p2mid][p2csym])
);
const maxDep2 = Math.min(MAX_DEPTH2, this.sol - this.length1);
if (prun >= maxDep2) {
return prun > maxDep2 ? 2 : 1;
}
let depth2;
for (depth2 = maxDep2 - 1; depth2 >= prun; depth2--) {
const ret = this.phase2(p2edge, p2esym, p2corn, p2csym, p2mid, depth2, this.depth1, 10);
if (ret < 0) {
break;
}
depth2 -= ret;
this.moveSol = [];
for (let i = 0; i < this.depth1 + depth2; i++) {
this.appendSolMove(this.move[i]);
}
for (let i = this.preMoveLen - 1; i >= 0; i--) {
this.appendSolMove(this.preMoves[i]);
}
this.sol = this.moveSol.length;
// FIXME
this.strSolution = this.solutionToString();
}
if (depth2 != maxDep2 - 1) {
//At least one solution has been found.
return this.probe >= this.probeMin ? 0 : 1;
} else {
return 1;
}
}
phase1(node: CoordCube, maxl: number, lm: number) {
if (node.prun == 0 && maxl < 5) {
if (this.allowShorter || maxl == 0) {
this.depth1 -= maxl;
const ret = this.initPhase2Pre();
this.depth1 += maxl;
return ret;
} else {
return 1;
}
}
for (let axis = 0; axis < 18; axis += 3) {
if (axis == lm || axis == lm - 9) {
continue;
}
for (let power = 0; power < 3; power++) {
const m = axis + power;
if (this.isRec && m != this.move[this.depth1 - maxl]) {
continue;
}
let prun = this.nodeUD[maxl].doMovePrun(node, m);
if (prun > maxl) {
break;
} else if (prun == maxl) {
continue;
}
if (USE_CONJ_PRUN) {
prun = this.nodeUD[maxl].doMovePrunConj(node, m);
if (prun > maxl) {
break;
} else if (prun == maxl) {
continue;
}
}
this.move[this.depth1 - maxl] = m;
this.valid1 = Math.min(this.valid1, this.depth1 - maxl);
const ret = this.phase1(this.nodeUD[maxl], maxl - 1, axis);
if (ret == 0) {
return 0;
} else if (ret == 2) {
break;
}
}
}
return 1;
}
appendSolMove(curMove: number) {
if (!this.moveSol || this.moveSol.length == 0) {
this.moveSol = [curMove];
return;
}
const axisCur = ~~(curMove / 3);
const axisLast = ~~(this.moveSol[this.moveSol.length - 1] / 3);
if (axisCur == axisLast) {
const pow = ((curMove % 3) + (this.moveSol[this.moveSol.length - 1] % 3) + 1) % 4;
if (pow == 3) {
this.moveSol.pop();
} else {
this.moveSol[this.moveSol.length - 1] = axisCur * 3 + pow;
}
return;
}
if (
this.moveSol.length > 1 &&
axisCur % 3 == axisLast % 3 &&
axisCur == ~~(this.moveSol[this.moveSol.length - 2] / 3)
) {
const pow = ((curMove % 3) + (this.moveSol[this.moveSol.length - 2] % 3) + 1) % 4;
if (pow == 3) {
this.moveSol[this.moveSol.length - 2] = this.moveSol[this.moveSol.length - 1];
this.moveSol.pop();
} else {
this.moveSol[this.moveSol.length - 2] = axisCur * 3 + pow;
}
return;
}
this.moveSol.push(curMove);
}
phase2(
edge: number,
esym: number,
corn: number,
csym: number,
mid: number,
maxl: number,
depth: number,
lm: number
): number {
if (edge == 0 && corn == 0 && mid == 0) {
return maxl;
}
const moveMask = ckmv2bit[lm];
for (let m = 0; m < 10; m++) {
if (((moveMask >> m) & 1) != 0) {
m += (0x42 >> m) & 3;
continue;
}
const midx = MPermMove![mid][m];
let cornx = CPermMove[corn][SymMoveUD[csym][m]];
const csymx = SymMult[cornx & 0xf][csym];
cornx >>= 4;
if (
getPruningMax(MCPermPrunMax, MCPermPrun, cornx * N_MPERM + MPermConj[midx][csymx]) >= maxl
) {
continue;
}
let edgex = EPermMove[edge][SymMoveUD[esym][m]];
const esymx = SymMult[edgex & 0xf][esym];
edgex >>= 4;
if (
getPruningMax(
EPermCCombPPrunMax,
EPermCCombPPrun,
edgex * N_COMB + CCombPConj[Perm2CombP[cornx] & 0xff][SymMultInv[esymx][csymx]]
) >= maxl
) {
continue;
}
const edgei = getPermSymInv(edgex, esymx, false);
const corni = getPermSymInv(cornx, csymx, true);
if (
getPruningMax(
EPermCCombPPrunMax,
EPermCCombPPrun,
(edgei >> 4) * N_COMB +
CCombPConj[Perm2CombP[corni >> 4] & 0xff][SymMultInv[edgei & 0xf][corni & 0xf]]
) >= maxl
) {
continue;
}
const ret = this.phase2(edgex, esymx, cornx, csymx, midx, maxl - 1, depth + 1, m);
if (ret >= 0) {
this.move[depth] = ud2std[m];
return ret;
}
}
return -1;
}
solutionToString() {
if (!this.moveSol) return "";
let sb = "";
const urf = (this.verbose & INVERSE_SOLUTION) != 0 ? (this.urfIdx + 3) % 6 : this.urfIdx;
if (urf < 3) {
for (let s = 0; s < this.moveSol.length; ++s) {
sb += move2str[urfMove[urf][this.moveSol[s]]] + " ";
}
} else {
for (let s = this.moveSol.length - 1; s >= 0; --s) {
sb += move2str[urfMove[urf][this.moveSol[s]]] + " ";
}
}
return sb;
}
}
//--------------------------------------------
let PARTIAL_INIT_LEVEL = 2;
const USE_COMBP_PRUN = true; //USE_TWIST_FLIP_PRUN;
const Ux1 = 0;
const Ux2 = 1;
const Ux3 = 2;
const Rx1 = 3;
const Rx2 = 4;
const Rx3 = 5;
const Fx1 = 6;
const Fx2 = 7;
const Fx3 = 8;
const Dx1 = 9;
const Dx2 = 10;
const Dx3 = 11;
const Lx1 = 12;
const Lx2 = 13;
const Lx3 = 14;
const Bx1 = 15;
const Bx2 = 16;
const Bx3 = 17;
const N_MOVES = 18;
const N_MOVES2 = 10;
const N_FLIP = 2048;
const N_FLIP_SYM = 336;
const N_TWIST = 2187;
const N_TWIST_SYM = 324;
const N_PERM = 40320;
const N_PERM_SYM = 2768;
const N_MPERM = 24;
const N_SLICE = 495;
const N_COMB = USE_COMBP_PRUN ? 140 : 70;
const P2_PARITY_MOVE = USE_COMBP_PRUN ? 0xa5 : 0;
const ud2std = [
Ux1,
Ux2,
Ux3,
Rx2,
Fx2,
Dx1,
Dx2,
Dx3,
Lx2,
Bx2,
Rx1,
Rx3,
Fx1,
Fx3,
Lx1,
Lx3,
Bx1,
Bx3,
];
const std2ud: number[] = [];
const ckmv2bit: number[] = [];
{
// init util
for (let i = 0; i < 18; i++) {
std2ud[ud2std[i]] = i;
}
for (let i = 0; i < 10; i++) {
const ix = ~~(ud2std[i] / 3);
ckmv2bit[i] = 0;
for (let j = 0; j < 10; j++) {
const jx = ~~(ud2std[j] / 3);
ckmv2bit[i] |= (ix == jx || (ix % 3 == jx % 3 && ix >= jx) ? 1 : 0) << j;
}
}
ckmv2bit[10] = 0;
}
//phase2
const CPermMove: number[][] = [];
const EPermMove: number[][] = [];
let MPermMove: number[][] | null = [];
const MPermConj: number[][] = [];
const CCombPMove: number[][] = []; // = new char[N_COMB][N_MOVES2];
const CCombPConj: number[][] = [];
const MCPermPrun: number[] = [];
const EPermCCombPPrun: number[] = [];
let TwistFlipPrunMax = 15;
let UDSliceFlipPrunMax = 15;
let MCPermPrunMax = 15;
let EPermCCombPPrunMax = 15;
{
//init move cubes
for (let i = 0; i < 18; i++) {
moveCube[i] = new CubieCube();
}
moveCube[0].initCoord(15120, 0, 119750400, 0);
moveCube[3].initCoord(21021, 1494, 323403417, 0);
moveCube[6].initCoord(8064, 1236, 29441808, 550);
moveCube[9].initCoord(9, 0, 5880, 0);
moveCube[12].initCoord(1230, 412, 2949660, 0);
moveCube[15].initCoord(224, 137, 328552, 137);
for (let a = 0; a < 18; a += 3) {
for (let p = 0; p < 2; p++) {
CubieCube.EdgeMult(moveCube[a + p], moveCube[a], moveCube[a + p + 1]);
CubieCube.CornMult(moveCube[a + p], moveCube[a], moveCube[a + p + 1]);
}
}
CubieCube.urf1 = new CubieCube().initCoord(2531, 1373, 67026819, 1367);
CubieCube.urf2 = new CubieCube().initCoord(2089, 1906, 322752913, 2040);
}
function initBasic() {
let c = new CubieCube();
{
//init sym cubes
const d = new CubieCube();
const f2 = new CubieCube().initCoord(28783, 0, 259268407, 0);
const u4 = new CubieCube().initCoord(15138, 0, 119765538, 7);
const lr2 = new CubieCube().initCoord(5167, 0, 83473207, 0);
for (let i = 0; i < 8; i++) {
lr2.ca[i] |= 3 << 3;
}
for (let i = 0; i < 16; i++) {
SymCube[i] = new CubieCube().init(c.ca, c.ea);
CubieCube.CornMultFull(c, u4, d);
CubieCube.EdgeMult(c, u4, d);
c.init(d.ca, d.ea);
if (i % 4 == 3) {
CubieCube.CornMultFull(c, lr2, d);
CubieCube.EdgeMult(c, lr2, d);
c.init(d.ca, d.ea);
}
if (i % 8 == 7) {
CubieCube.CornMultFull(c, f2, d);
CubieCube.EdgeMult(c, f2, d);
c.init(d.ca, d.ea);
}
}
}
{
// gen sym tables
for (let i = 0; i < 16; i++) {
SymMult[i] = [];
SymMultInv[i] = [];
SymMove[i] = [];
Sym8Move[i] = 0;
SymMoveUD[i] = [];
}
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
SymMult[i][j] = i ^ j ^ ((0x14ab4 >> j) & (i << 1) & 2); // SymMult[i][j] = (i ^ j ^ (0x14ab4 >> j & i << 1 & 2)));
SymMultInv[SymMult[i][j]][j] = i;
}
}
c = new CubieCube();
for (let s = 0; s < 16; s++) {
for (let j = 0; j < 18; j++) {
CubieCube.CornConjugate(moveCube[j], SymMultInv[0][s], c);
outloop: for (let m = 0; m < 18; m++) {
for (let t = 0; t < 8; t++) {
if (moveCube[m].ca[t] != c.ca[t]) {
continue outloop;
}
}
SymMove[s][j] = m;
SymMoveUD[s][std2ud[j]] = std2ud[m];
break;
}
if (s % 2 == 0) {
Sym8Move[(j << 3) | (s >> 1)] = SymMove[s][j];
}
}
}
}
{
// init sym 2 raw tables
function initSym2Raw(
N_RAW: number,
Sym2Raw: number[],
Raw2Sym: number[],
SymState: number[],
coord: number,
setFunc: Function,
getFunc: Function
) {
const N_RAW_HALF = (N_RAW + 1) >> 1;
const c = new CubieCube();
const d = new CubieCube();
let count = 0;
const sym_inc = coord >= 2 ? 1 : 2;
const conjFunc = coord != 1 ? CubieCube.EdgeConjugate : CubieCube.CornConjugate;
for (let i = 0; i < N_RAW; i++) {
if (Raw2Sym[i] !== undefined) {
continue;
}
setFunc.call(c, i);
for (let s = 0; s < 16; s += sym_inc) {
conjFunc(c, s, d);
const idx = getFunc.call(d);
if (USE_TWIST_FLIP_PRUN && coord == 0) {
FlipS2RF[(count << 3) | (s >> 1)] = idx;
}
if (idx == i) {
SymState[count] |= 1 << (s / sym_inc);
}
Raw2Sym[idx] = ((count << 4) | s) / sym_inc;
}
Sym2Raw[count++] = i;
}
return count;
}
initSym2Raw(
N_FLIP,
FlipS2R,
FlipR2S,
SymStateFlip,
0,
CubieCube.prototype.setFlip,
CubieCube.prototype.getFlip
);
initSym2Raw(
N_TWIST,
TwistS2R,
TwistR2S,
SymStateTwist,
1,
CubieCube.prototype.setTwist,
CubieCube.prototype.getTwist
);
initSym2Raw(
N_PERM,
EPermS2R,
EPermR2S,
SymStatePerm,
2,
CubieCube.prototype.setEPerm,
CubieCube.prototype.getEPerm
);
const cc = new CubieCube();
for (let i = 0; i < N_PERM_SYM; i++) {
setNPerm(cc.ea, EPermS2R[i], 8, true);
Perm2CombP[i] =
getComb(cc.ea, 0, true) + (USE_COMBP_PRUN ? getNParity(EPermS2R[i], 8) * 70 : 0);
c.invFrom(cc);
PermInvEdgeSym[i] = EPermR2S[c.getEPerm()];
}
}
{
// init coord tables
c = new CubieCube();
const d = new CubieCube();
function initSymMoveTable(
moveTable: number[][],
SymS2R: number[],
N_SIZE: number,
N_MOVES: number,
// setFunc: Function,
// getFunc: Function,
// multFunc: Function,
setFunc: keyof CubieCube,
getFunc: keyof CubieCube,
multFunc: Function,
ud2std?: number[]
) {
for (let i = 0; i < N_SIZE; i++) {
moveTable[i] = [];
(c[setFunc] as any)(SymS2R[i]);
// setFunc.call(c, SymS2R[i]);
for (let j = 0; j < N_MOVES; j++) {
multFunc(c, moveCube[ud2std ? ud2std[j] : j], d);
moveTable[i][j] = (d[getFunc] as any)();
}
}
}
initSymMoveTable(
FlipMove,
FlipS2R,
N_FLIP_SYM,
N_MOVES,
"setFlip",
"getFlipSym",
// CubieCube.prototype.setFlip,
// CubieCube.prototype.getFlipSym,
CubieCube.EdgeMult
);
initSymMoveTable(
TwistMove,
TwistS2R,
N_TWIST_SYM,
N_MOVES,
"setTwist",
"getTwistSym",
// CubieCube.setTwist,
// CubieCube.getTwistSym,
CubieCube.CornMult
);
initSymMoveTable(
EPermMove,
EPermS2R,
N_PERM_SYM,
N_MOVES2,
"setEPerm",
"getEPermSym",
// CubieCube.prototype.setEPerm,
// CubieCube.prototype.getEPermSym,
CubieCube.EdgeMult,
ud2std
);
initSymMoveTable(
CPermMove,
EPermS2R,
N_PERM_SYM,
N_MOVES2,
"setCPerm",
"getCPermSym",
// CubieCube.prototype.setCPerm,
// CubieCube.prototype.getCPermSym,
CubieCube.CornMult,
ud2std
);
for (let i = 0; i < N_SLICE; i++) {
UDSliceMove[i] = [];
UDSliceConj[i] = [];
c.setUDSlice(i);
for (let j = 0; j < N_MOVES; j++) {
CubieCube.EdgeMult(c, moveCube[j], d);
UDSliceMove[i][j] = d.getUDSlice();
}
for (let j = 0; j < 16; j += 2) {
CubieCube.EdgeConjugate(c, SymMultInv[0][j], d);
UDSliceConj[i][j >> 1] = d.getUDSlice();
}
}
MPermMove = [];
for (let i = 0; i < N_MPERM; i++) {
MPermMove[i] = [];
MPermConj[i] = [];
c.setMPerm(i);
for (let j = 0; j < N_MOVES2; j++) {
CubieCube.EdgeMult(c, moveCube[ud2std[j]], d);
MPermMove[i][j] = d.getMPerm();
}
for (let j = 0; j < 16; j++) {
CubieCube.EdgeConjugate(c, SymMultInv[0][j], d);
MPermConj[i][j] = d.getMPerm();
}
}
for (let i = 0; i < N_COMB; i++) {
CCombPMove[i] = [];
CCombPConj[i] = [];
c.setCComb(i % 70);
for (let j = 0; j < N_MOVES2; j++) {
CubieCube.CornMult(c, moveCube[ud2std[j]], d);
CCombPMove[i][j] = d.getCComb() + 70 * (((P2_PARITY_MOVE >> j) & 1) ^ ~~(i / 70));
}
for (let j = 0; j < 16; j++) {
CubieCube.CornConjugate(c, SymMultInv[0][j], d);
CCombPConj[i][j] = d.getCComb() + 70 * ~~(i / 70);
}
}
}
}
//init pruning tables
let InitPrunProgress = -1;
function doInitPrunTables(targetProgress: number) {
if (USE_TWIST_FLIP_PRUN) {
TwistFlipPrunMax = initRawSymPrun(
TwistFlipPrun,
2048,
324,
null,
null,
TwistMove,
SymStateTwist,
0x19603
);
}
if (InitPrunProgress > targetProgress) {
return;
}
CoordCube.UDSliceTwistPrunMax = initRawSymPrun(
UDSliceTwistPrun,
495,
324,
UDSliceMove,
UDSliceConj,
TwistMove,
SymStateTwist,
0x69603
);
if (InitPrunProgress > targetProgress) {
return;
}
UDSliceFlipPrunMax = initRawSymPrun(
UDSliceFlipPrun,
495,
336,
UDSliceMove,
UDSliceConj,
FlipMove,
SymStateFlip,
0x69603
);
if (InitPrunProgress > targetProgress) {
return;
}
MCPermPrunMax = initRawSymPrun(
MCPermPrun,
24,
2768,
MPermMove,
MPermConj,
CPermMove,
SymStatePerm,
0x8ea34
);
if (InitPrunProgress > targetProgress) {
return;
}
EPermCCombPPrunMax = initRawSymPrun(
EPermCCombPPrun,
N_COMB,
2768,
CCombPMove,
CCombPConj,
EPermMove,
SymStatePerm,
0x7d824
);
}
function initPrunTables() {
if (InitPrunProgress < 0) {
initBasic();
InitPrunProgress = 0;
}
if (InitPrunProgress == 0) {
doInitPrunTables(99);
} else if (InitPrunProgress < 54) {
doInitPrunTables(InitPrunProgress);
} else {
return true;
}
return false;
}
export function randomCube() {
let ep, cp;
const eo = ~~(Math.random() * 2048);
const co = ~~(Math.random() * 2187);
do {
ep = ~~(Math.random() * fact[12]);
cp = ~~(Math.random() * fact[8]);
} while (getNParity(cp, 8) != getNParity(ep, 12));
const cc = new CubieCube().initCoord(cp, co, ep, eo);
return cc.toFaceCube();
}
export function solve(facelet: string): string {
return new Search().solution(facelet);
}
export function initFull() {
PARTIAL_INIT_LEVEL = 0;
initPrunTables();
}