cubing
Version:
A collection of JavaScript cubing libraries.
831 lines (830 loc) • 21.2 kB
JavaScript
// src/cubing/vendor/mpl/xyzzy/master_tetraminx-solver.js
import { randomUIntBelow } from "random-uint-below";
function factorial(n) {
if (n < 2) {
return n;
}
let f = 1;
for (let i = 2; i <= n; i++) {
f *= i;
}
return f;
}
function identity_permutation(n) {
let a = Array(n);
for (let i = 0; i < n; i++) {
a[i] = i;
}
return a;
}
function permutation_to_index(perm) {
perm = perm.slice();
let n = perm.length;
let f = factorial(n - 1);
let ind = 0;
while (n > 1) {
n--;
let e = perm[0];
ind += e * f;
for (let i = 0; i < n; i++) {
let x = perm[i + 1];
perm[i] = x - (x > e);
}
f /= n;
}
return ind;
}
function index_to_evenpermutation(ind, n) {
let perm = [];
let f = factorial(n - 1) / 2;
let parity = 0;
for (let i = 0; i < n - 1; i++) {
perm[i] = ind / f | 0;
ind %= f;
f /= n - 1 - i;
}
perm[n - 1] = 0;
for (let i = n - 2; i >= 0; i--) {
for (let j = i + 1; j < n; j++) {
if (perm[j] >= perm[i]) {
perm[j]++;
} else {
parity ^= 1;
}
}
}
if (parity === 1) {
[perm[n - 2], perm[n - 1]] = [perm[n - 1], perm[n - 2]];
}
return perm;
}
function evenpermutation_to_index(perm) {
return permutation_to_index(perm) >> 1;
}
var [evenpermutation12_to_index, index_to_evenpermutation12] = (() => {
let index_in_set_bits = new Int8Array(4096 * 12);
let look_up_set_bits = new Int8Array(4096 * 12);
for (let i = 0; i < 4096; i++) {
for (let j = 0, counter = 0; j < 12; j++) {
if ((i >>> j & 1) === 0) {
continue;
}
index_in_set_bits[j << 12 | i] = counter;
look_up_set_bits[counter << 12 | i] = j;
counter++;
}
}
function evenpermutation12_to_index2(perm) {
let unused = 4095;
let f = 19958400;
let ind = 0;
for (let i = 0; i < 10; i++) {
let v = perm[i];
ind += index_in_set_bits[unused | v << 12] * f;
unused &= ~(1 << v);
f /= 11 - i;
}
return ind;
}
function index_to_evenpermutation122(ind, perm) {
let unused = 4095;
let f = 19958400;
let parity = 0;
for (let i = 0; i < 10; i++) {
let a = ind / f | 0;
ind -= a * f;
parity ^= a & 1;
let v = look_up_set_bits[unused | a << 12];
perm[i] = v;
unused &= ~(1 << v);
f /= 11 - i;
}
perm[10] = look_up_set_bits[unused | parity << 12];
perm[11] = look_up_set_bits[unused | (parity ^ 1) << 12];
return perm;
}
return [evenpermutation12_to_index2, index_to_evenpermutation122];
})();
function compose(A, B) {
let C = [];
for (let i = 0; i < B.length; i++) {
C[i] = A[B[i]];
}
return C;
}
function invert(perm) {
let inv = [];
for (let i = 0; i < perm.length; i++) {
inv[perm[i]] = i;
}
return inv;
}
function permutation_from_cycle(cycle, n) {
let perm = [];
for (let i = 0; i < n; i++) {
perm[i] = i;
}
for (let i = 0; i < cycle.length; i++) {
perm[cycle[i]] = cycle[(i + 1) % cycle.length];
}
return perm;
}
function permutation_from_cycles(cycles, n) {
if (cycles.length === 0) {
return identity_permutation(n);
}
return cycles.map((cycle) => permutation_from_cycle(cycle, n)).reduce(compose);
}
function compose_state(state1, state2) {
let co = Array(4);
for (let i = 0; i < 4; i++) {
co[i] = (state1.co[i] + state2.co[i]) % 3;
}
let mp = compose(state1.mp, state2.mp);
let wp = compose(state1.wp, state2.wp);
let cp = compose(state1.cp, state2.cp);
return { co, mp, wp, cp };
}
var solved = {
co: [0, 0, 0, 0],
mp: identity_permutation(12),
wp: identity_permutation(12),
cp: [0, 1, 2, 3]
};
var move_U = {
co: [2, 0, 0, 0],
mp: identity_permutation(12),
wp: permutation_from_cycle([1, 9, 11], 12),
cp: [0, 1, 2, 3]
};
var move_L = {
co: [0, 2, 0, 0],
mp: identity_permutation(12),
wp: permutation_from_cycle([0, 7, 2], 12),
cp: [0, 1, 2, 3]
};
var move_R = {
co: [0, 0, 2, 0],
mp: identity_permutation(12),
wp: permutation_from_cycle([3, 6, 10], 12),
cp: [0, 1, 2, 3]
};
var move_B = {
co: [0, 0, 0, 2],
mp: identity_permutation(12),
wp: permutation_from_cycle([4, 8, 5], 12),
cp: [0, 1, 2, 3]
};
var move_Uw = {
co: [2, 0, 0, 0],
mp: permutation_from_cycles(
[
[1, 9, 11],
[7, 3, 5]
],
12
),
wp: permutation_from_cycles(
[
[1, 9, 11],
[7, 3, 5]
],
12
),
cp: [0, 2, 3, 1]
};
var move_Lw = {
co: [0, 2, 0, 0],
mp: permutation_from_cycles(
[
[0, 7, 2],
[6, 1, 8]
],
12
),
wp: permutation_from_cycles(
[
[0, 7, 2],
[6, 1, 8]
],
12
),
cp: [3, 1, 0, 2]
};
var move_Rw = {
co: [0, 0, 2, 0],
mp: permutation_from_cycles(
[
[3, 6, 10],
[9, 0, 4]
],
12
),
wp: permutation_from_cycles(
[
[3, 6, 10],
[9, 0, 4]
],
12
),
cp: [1, 3, 2, 0]
};
var move_Bw = {
co: [0, 0, 0, 2],
mp: permutation_from_cycles(
[
[4, 8, 5],
[10, 2, 11]
],
12
),
wp: permutation_from_cycles(
[
[4, 8, 5],
[10, 2, 11]
],
12
),
cp: [2, 0, 1, 3]
};
var moves = [
move_Uw,
move_Lw,
move_Rw,
move_Bw,
move_U,
move_L,
move_R,
move_B
];
var move_names = ["u", "l", "r", "b", "U", "L", "R", "B"];
var N_MOVES = 8;
var N_MOVES_PHASE2 = 4;
function moves_commute(i, j) {
if (i >= 4 && j >= 4) {
return true;
}
if (i < 4 && j < 4) {
return i === j;
}
return (i ^ j) === 4;
}
function stringify_move_sequence(move_sequence) {
let suffixes = ["0", "", "'"];
let s = move_sequence.map(([m, r]) => move_names[m] + suffixes[r]);
return s.join(" ");
}
function generate_random_state() {
let co = Array(4);
for (let i = 0; i < 4; i++) {
co[i] = randomUIntBelow(3);
}
let mp = index_to_evenpermutation(randomUIntBelow(factorial(6) / 2), 6);
for (let i = 0, parity = 0; i < 6; i++) {
let eo = i === 5 ? parity : randomUIntBelow(2);
parity ^= eo;
mp[i] += eo * 6;
mp[i + 6] = (mp[i] + 6) % 12;
}
let wp = index_to_evenpermutation(randomUIntBelow(factorial(12) / 2), 12);
let cp = index_to_evenpermutation(randomUIntBelow(factorial(4) / 2), 4);
return { co, mp, wp, cp };
}
function generate_random_state_scramble() {
return solve(generate_random_state(randomUIntBelow));
}
function generate_scramble_sequence(tips = true, obfuscate_tips = false) {
let scramble_string = stringify_move_sequence(
generate_random_state_scramble()
);
if (!tips) {
return scramble_string;
}
let tip_names = ["u", "l", "r", "b"];
let suffixes = ["0", "", "'"];
if (!obfuscate_tips) {
for (let i = 0; i < 4; i++) {
let x = randomUIntBelow(3);
if (x !== 0) {
scramble_string += ` ${tip_names[i]}${suffixes[x]}`;
}
}
return scramble_string.trim();
}
let amount = [];
let amount_pre = [];
let amount_post = [];
for (let i = 0; i < 4; i++) {
amount[i] = randomUIntBelow(3);
amount_pre[i] = randomUIntBelow(3);
amount_post[i] = (amount[i] - amount_pre[i] + 3) % 3;
}
let weight = (arr) => arr.filter((x) => x !== 0).length;
while (!(weight(amount_pre) >= 1 && weight(amount_post) >= 1 && weight(amount_pre) + weight(amount_post) >= 4)) {
for (let i = 0; i < 4; i++) {
amount_pre[i] = randomUIntBelow(3);
amount_post[i] = (amount[i] - amount_pre[i] + 3) % 3;
}
}
let prepend = amount_pre.map((x, i) => x !== 0 ? `${tip_names[i]}${suffixes[x]} ` : "").join("");
let append = amount_post.map((x, i) => x !== 0 ? ` ${tip_names[i]}${suffixes[x]}` : "").join("");
return prepend + scramble_string + append;
}
function solve(state) {
let phase1_indices = index_phase1(state);
let phase2_mtables = [
generate_phase2_permutation_mtable(),
generate_phase2_orientation_mtable()
];
let phase2_ptables = [
generate_phase2_permutation_ptable(),
generate_phase2_orientation_ptable()
];
let phase1gen = phase1_ida_solve_gen(phase1_indices);
let best = void 0;
let intermediate_states = /* @__PURE__ */ new Set();
let start_time = performance.now();
for (let i = 0; i < 22; i++) {
let { value: sol1, done } = phase1gen.next();
let new_state = state;
for (let [m, r] of sol1) {
for (let i2 = 0; i2 < r; i2++) {
new_state = compose_state(new_state, moves[m]);
}
}
let stringified_state = JSON.stringify(new_state);
if (intermediate_states.has(stringified_state)) {
continue;
} else {
intermediate_states.add(stringified_state);
}
let phase2_indices = index_phase2(new_state);
let moves_left = best ? best.length - sol1.length - 1 : 999999;
let sol2 = ida_solve_gen(
phase2_indices,
phase2_mtables,
phase2_ptables,
moves_left
).next().value;
if (sol2 === void 0) {
continue;
}
if (best === void 0 || best.length > sol1.length + sol2.length) {
best = sol1.concat(sol2);
}
if (performance.now() - start_time > 300) {
break;
}
}
return best;
}
function determine_V_coset(p) {
return p[3 ^ p.indexOf(3)];
}
function index_phase1(state) {
let w = compose(invert(state.mp), state.wp);
let c = (state.co.reduce((x, y) => x + y) - determine_V_coset(state.cp) + 3) % 3;
return [0, 1, 2, 3, 4, 5].map(
(i) => i + 6 * w.indexOf(i) + 72 * w.indexOf(i + 6) + 864 * c
);
}
var phase1_permtable_m = [];
var phase1_permtable_minv = [];
var phase1_permtable_w = [];
var phase1_permtable_winv = [];
for (let i = 0; i < N_MOVES; i++) {
let move = moves[i];
phase1_permtable_m[i] = move.mp;
phase1_permtable_minv[i] = invert(move.mp);
phase1_permtable_w[i] = move.wp;
phase1_permtable_winv[i] = invert(move.wp);
}
var phase1_c_update = [0, 0, 0, 0, 2, 2, 2, 2];
var phase1_score_ptable = [
//-12 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 12
[
14,
-1,
-1,
11,
11,
10,
9,
8,
8,
7,
7,
6,
4,
5,
5,
3,
4,
4,
2,
3,
4,
3,
-1,
-1,
0
],
[
13,
-1,
-1,
11,
10,
10,
9,
8,
8,
7,
7,
6,
4,
5,
5,
3,
4,
4,
2,
3,
3,
1,
-1,
-1,
6
]
];
var phase1_score_ptable_condensed = new Int8Array(55);
for (let i = 0; i < 25; i++) {
phase1_score_ptable_condensed[i] = phase1_score_ptable[0][i];
phase1_score_ptable_condensed[i + 30] = phase1_score_ptable[1][i];
}
var phase1_coord_to_score = new Int8Array(6 * 12 * 12 * 3);
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 12; j++) {
for (let k = 0; k < 12; k++) {
let index = i + 6 * j + 72 * k;
let score = 2;
if (j === i) {
score++;
} else if (j === (i + 6) % 12) {
score--;
}
if (k === (i + 6) % 12) {
score++;
} else if (k === i) {
score--;
}
phase1_coord_to_score[index] = score;
phase1_coord_to_score[index + 6 * 12 * 12] = phase1_coord_to_score[index + 2 * 6 * 12 * 12] = score + 5;
}
}
}
function* phase1_ida_solve_gen(coords) {
let bound = 0;
let mtable = generate_phase1_pairingc_mtable();
let ptable = generate_phase1_pairing2c_ptable();
while (true) {
yield* phase1_ida_search_gen(...coords, mtable, ptable, bound, -1);
bound++;
}
}
function* phase1_ida_search_gen(a, b, c, d, e, f, mtable, ptable, bound, last) {
let nmoves = N_MOVES;
let score = phase1_coord_to_score[a] + phase1_coord_to_score[b] + phase1_coord_to_score[c] + phase1_coord_to_score[d] + phase1_coord_to_score[e] + phase1_coord_to_score[f];
let heuristic = Math.max(
ptable[a % 864 + b * 864],
ptable[c % 864 + b * 864],
ptable[e % 864 + b * 864],
ptable[a % 864 + d * 864],
ptable[c % 864 + d * 864],
ptable[e % 864 + d * 864],
ptable[a % 864 + f * 864],
ptable[c % 864 + f * 864],
ptable[e % 864 + f * 864],
ptable[a % 864 + c * 864],
ptable[a % 864 + e * 864],
ptable[c % 864 + e * 864],
ptable[b % 864 + d * 864],
ptable[b % 864 + f * 864],
ptable[d % 864 + f * 864],
phase1_score_ptable_condensed[score]
);
if (heuristic > bound) {
return;
}
if (bound === 0) {
yield [];
return;
}
if (heuristic === 0 && bound === 1) {
return;
}
for (let m = 0; m < nmoves; m++) {
if (m === last) {
continue;
}
if (m < last && moves_commute(m, last)) {
continue;
}
let A = a;
let B = b;
let C = c;
let D = d;
let E = e;
let F = f;
for (let r = 1; r <= 2; r++) {
A = mtable[A][m];
B = mtable[B][m];
C = mtable[C][m];
D = mtable[D][m];
E = mtable[E][m];
F = mtable[F][m];
let subpath_gen = phase1_ida_search_gen(
A,
B,
C,
D,
E,
F,
mtable,
ptable,
bound - 1,
m
);
while (true) {
let { value: subpath, done } = subpath_gen.next();
if (done) {
break;
}
yield [[m, r]].concat(subpath);
}
}
}
}
function index_phase2(state) {
let edges = state.mp;
let ep = evenpermutation_to_index(edges.slice(0, 6).map((x) => x % 6));
let eo = edges.slice(0, 5).map((x, i) => (x >= 6) * 2 ** i).reduce((x, y) => x + y);
let co = state.co.map((x, i) => x * 3 ** i).reduce((x, y) => x + y);
let cloc = state.cp.indexOf(0);
return [ep + 360 * cloc, eo + 32 * co];
}
var tables = {};
function generate_phase1_pairing_mtable() {
if (tables.phase1pm) {
return tables.phase1pm;
}
let mtable = Array(6 * 12 * 12).fill().map(() => Array(N_MOVES).fill(-1));
for (let midge = 0; midge < 6; midge++) {
for (let wingl = 0; wingl < 12; wingl++) {
for (let wingh = 0; wingh < 12; wingh++) {
if (wingl === wingh) {
continue;
}
let index = midge + 6 * wingl + 72 * wingh;
for (let m = 0; m < N_MOVES; m++) {
let new_midge = phase1_permtable_minv[m][midge];
let new_wingl = phase1_permtable_winv[m][wingl];
let new_wingh = phase1_permtable_winv[m][wingh];
if (new_midge < 6) {
mtable[index][m] = new_midge + 6 * new_wingl + 72 * new_wingh;
} else {
mtable[index][m] = new_midge - 6 + 6 * new_wingh + 72 * new_wingl;
}
}
}
}
}
return tables.phase1pm = mtable;
}
function generate_phase1_pairingc_mtable() {
if (tables.phase1pcm) {
return tables.phase1pcm;
}
let mtable_pairing = generate_phase1_pairing_mtable();
let mtable = Array(mtable_pairing.length * 3).fill().map(() => Array(N_MOVES).fill(-1));
for (let index = 0; index < mtable_pairing.length; index++) {
for (let m = 0; m < N_MOVES; m++) {
let new_index = mtable_pairing[index][m];
mtable[index][m] = new_index + 6 * 12 * 12 * phase1_c_update[m];
mtable[index + 6 * 12 * 12][m] = new_index + 6 * 12 * 12 * ((phase1_c_update[m] + 1) % 3);
mtable[index + 2 * 6 * 12 * 12][m] = new_index + 6 * 12 * 12 * ((phase1_c_update[m] + 2) % 3);
}
}
return tables.phase1pcm = mtable;
}
function generate_phase1_pairing2c_ptable() {
if (tables.phase1p2cp) {
return tables.phase1p2cp;
}
let mtable_noc = generate_phase1_pairing_mtable();
let mtable = generate_phase1_pairingc_mtable();
let ptable = new Int8Array((6 * 12 * 12) ** 2 * 3);
ptable.fill(-1);
let g = [0, 1, 2, 3, 4, 5].map((x) => x + 6 * x + 72 * (x + 6));
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 6; j++) {
if (i === j) {
continue;
}
ptable[g[i] + 864 * g[j]] = 0;
}
}
let dist = 0;
while (true) {
let changed = false;
for (let index = 0; index < ptable.length; index++) {
if (ptable[index] !== dist) {
continue;
}
let index0 = index % 864;
let index1 = Math.floor(index / 864);
for (let m = 0; m < N_MOVES; m++) {
let new_index0 = index0;
let new_index1 = index1;
for (let r = 1; r <= 2; r++) {
new_index0 = mtable_noc[new_index0][m];
new_index1 = mtable[new_index1][m];
let new_index = new_index0 + 864 * new_index1;
if (ptable[new_index] === -1) {
changed = true;
ptable[new_index] = dist + 1;
}
}
}
}
if (!changed) {
break;
}
dist++;
}
return tables.phase1p2cp = ptable;
}
function generate_phase2_permutation_mtable() {
if (tables.phase2pm) {
return tables.phase2pm;
}
let mtable = Array(1440).fill().map(() => Array(N_MOVES_PHASE2));
for (let ep = 0; ep < 360; ep++) {
let perm = index_to_evenpermutation(ep, 6);
for (let i = 0; i < 6; i++) {
perm[i + 6] = perm[i] + 6;
}
for (let m = 0; m < N_MOVES_PHASE2; m++) {
let new_perm = compose(perm, moves[m].mp);
let new_ep = evenpermutation_to_index(
new_perm.slice(0, 6).map((x) => x % 6)
);
for (let new_cloc = 0; new_cloc < 4; new_cloc++) {
let cloc = moves[m].cp[new_cloc];
mtable[ep + 360 * cloc][m] = new_ep + 360 * new_cloc;
}
}
}
return tables.phase2pm = mtable;
}
function generate_phase2_orientation_mtable() {
if (tables.phase2om) {
return tables.phase2om;
}
let mtable = Array(32 * 81).fill().map(() => Array(N_MOVES_PHASE2));
for (let eo = 0; eo < 32; eo++) {
let eo_array = [0, 1, 2, 3, 4].map((i) => eo >> i & 1);
eo_array[5] = eo_array.reduce((x, y) => x ^ y);
let perm = [];
for (let i = 0; i < 6; i++) {
perm[i] = i + 6 * eo_array[i];
perm[i + 6] = i + 6 * (eo_array[i] ^ 1);
}
for (let co = 0; co < 81; co++) {
let co_array = [0, 1, 2, 3].map((i) => Math.floor(co / 3 ** i) % 3);
for (let m = 0; m < N_MOVES_PHASE2; m++) {
let new_perm = compose(perm, moves[m].mp);
let new_eo_array = new_perm.slice(0, 5).map((x) => +(x >= 6));
let new_eo = 0;
for (let i = 0; i < 5; i++) {
new_eo += new_eo_array[i] << i;
}
let new_co_array = co_array.map((x, i) => (x + moves[m].co[i]) % 3);
let new_co = 0;
for (let i = 0; i < 4; i++) {
new_co += new_co_array[i] * 3 ** i;
}
mtable[eo + 32 * co][m] = new_eo + 32 * new_co;
}
}
}
return tables.phase2om = mtable;
}
function generate_phase2_permutation_ptable() {
if (tables.phase2pp) {
return tables.phase2pp;
}
return tables.phase2pp = bfs(generate_phase2_permutation_mtable(), [0]);
}
function generate_phase2_orientation_ptable() {
if (tables.phase2op) {
return tables.phase2op;
}
return tables.phase2op = bfs(generate_phase2_orientation_mtable(), [0]);
}
function bfs(mtable, goal_states) {
let N = mtable.length;
let nmoves = mtable[0].length;
let ptable = Array(N).fill(-1);
let queue = goal_states.slice();
let new_queue = [];
let depth = 0;
while (queue.length > 0) {
new_queue.length = 0;
for (let state of queue) {
if (ptable[state] !== -1) {
continue;
}
ptable[state] = depth;
for (let move_index = 0; move_index < nmoves; move_index++) {
let new_state = mtable[state][move_index];
while (new_state !== state) {
new_queue.push(new_state);
new_state = mtable[new_state][move_index];
}
}
}
[queue, new_queue] = [new_queue, queue];
depth += 1;
}
return ptable;
}
function* ida_solve_gen(indices, mtables, ptables, moves_left) {
let ncoords = indices.length;
let bound = 0;
for (let i = 0; i < ncoords; i++) {
bound = Math.max(bound, ptables[i][indices[i]]);
}
while (bound <= moves_left) {
yield* ida_search_gen(indices, mtables, ptables, bound, -1);
bound++;
}
}
function* ida_search_gen(indices, mtables, ptables, bound, last) {
let ncoords = indices.length;
let nmoves = mtables[0][0].length;
let heuristic = 0;
for (let i = 0; i < ncoords; i++) {
heuristic = Math.max(heuristic, ptables[i][indices[i]]);
}
if (heuristic > bound) {
return;
}
if (bound === 0) {
yield [];
return;
}
if (heuristic === 0 && bound === 1) {
return;
}
for (let m = 0; m < nmoves; m++) {
if (m === last) {
continue;
}
if (m < last && moves_commute(m, last)) {
continue;
}
let new_indices = indices.slice();
for (let c = 0; c < ncoords; c++) {
new_indices[c] = mtables[c][indices[c]][m];
}
let r = 1;
while (indices.some((_, i) => indices[i] !== new_indices[i])) {
let subpath_gen = ida_search_gen(
new_indices,
mtables,
ptables,
bound - 1,
m
);
while (true) {
let { value: subpath, done } = subpath_gen.next();
if (done) {
break;
}
yield [[m, r]].concat(subpath);
}
for (let c = 0; c < ncoords; c++) {
new_indices[c] = mtables[c][new_indices[c]][m];
}
r++;
}
}
}
async function randomMasterTetraminxScrambleString() {
return generate_scramble_sequence(false);
}
export {
randomMasterTetraminxScrambleString
};
//# sourceMappingURL=search-dynamic-solve-master_tetraminx-3D4MBF3V.js.map