UNPKG

cubing

Version:

A collection of JavaScript cubing libraries.

772 lines (770 loc) 20.1 kB
import { Alg } from "./chunk-3MMNI5O2.js"; // src/cubing/vendor/mpl/xyzzy/kilosolver.js import { randomUIntBelow } from "random-uint-below"; var PHASE4_THRESHOLD = 7; function factorial(n) { if (n < 2) { return n; } let f = 1; for (let i = 2; i <= n; i++) { f *= i; } return f; } function C(n, k) { if (k < 0 || k > n) { return 0; } if (k === 0 || k === n) { return 1; } let c = 1; for (let i = 0; i < k; i++) { c = c * (n - i) / (i + 1) | 0; } return c; } 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 permutation_parity(A) { let n = A.length; let parity = 0; for (let i = 0; i < n - 1; i++) { for (let j = i; j < n; j++) { if (A[i] > A[j]) { parity ^= 1; } } } return parity; } function evenpermutation_to_index(perm) { return permutation_to_index(perm) >> 1; } var [evenpermutation10_to_index, index_to_evenpermutation10] = (() => { let index_in_set_bits = new Int8Array(1024 * 10); let look_up_set_bits = new Int8Array(1024 * 10); for (let i = 0; i < 1024; i++) { for (let j = 0, counter = 0; j < 10; j++) { if ((i >>> j & 1) === 0) { continue; } index_in_set_bits[j << 10 | i] = counter; look_up_set_bits[counter << 10 | i] = j; counter++; } } function evenpermutation10_to_index2(perm) { let unused = 1023; let f = 181440; let ind = 0; for (let i = 0; i < 8; i++) { let v = perm[i]; ind += index_in_set_bits[unused | v << 10] * f; unused &= ~(1 << v); f /= 9 - i; } return ind; } function index_to_evenpermutation102(ind, perm) { let unused = 1023; let f = 181440; let parity = 0; for (let i = 0; i < 8; i++) { let a = ind / f | 0; ind -= a * f; parity ^= a & 1; let v = look_up_set_bits[unused | a << 10]; perm[i] = v; unused &= ~(1 << v); f /= 9 - i; } perm[8] = look_up_set_bits[unused | parity << 10]; perm[9] = look_up_set_bits[unused | (parity ^ 1) << 10]; return perm; } return [evenpermutation10_to_index2, index_to_evenpermutation102]; })(); function comb_to_index(l) { let bits = l.length; let ones = 0; for (let i = 0; i < bits; i++) { ones += +(l[i] === 1); } let zeros = bits - ones; if (zeros === 0 || ones === 0 || bits === 1) { return 0; } let b = C(bits - 1, ones); let ind = 0; for (let i = 0; zeros > 0 && ones > 0 && bits > 1; i++) { bits--; if (l[i] === 0) { b = b * --zeros / bits; } else { ind += b; b = b * ones-- / bits; } } return ind; } function index_to_comb(ind, ones, bits) { let zeros = bits - ones; let b = C(bits - 1, ones); let l = []; let n = bits - 1; for (let i = 0; i < n; i++) { bits--; if (ind < b) { l.push(0); b = b * --zeros / bits; } else { l.push(1); ind -= b; b = b * ones-- / bits; } } l.push(ones); return l; } function compose(A, B) { let C2 = []; for (let i = 0; i < B.length; i++) { C2[i] = A[B[i]]; } return C2; } function compose_o(A, B) { let p = compose(A[0], B[0]); let o = []; let n = B[0].length; for (let i = 0; i < n; i++) { o[i] = (A[1][B[0][i]] + B[1][i]) % 3; } return [p, o]; } 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 unsparsify_list(d, n) { let l = Array(n).fill(0); for (let k in d) { l[k] = d[k]; } return l; } var move_U = [ permutation_from_cycle([0, 1, 2, 3, 4], 20), unsparsify_list({}, 20) ]; var move_R = [ permutation_from_cycle([4, 3, 11, 12, 13], 20), unsparsify_list({ 4: 2, 3: 1, 11: 1, 12: 1, 13: 1 }, 20) ]; var move_F = [ permutation_from_cycle([3, 2, 9, 10, 11], 20), unsparsify_list({ 3: 2, 2: 1, 9: 1, 10: 1, 11: 1 }, 20) ]; var move_L = [ permutation_from_cycle([2, 1, 7, 8, 9], 20), unsparsify_list({ 2: 2, 1: 1, 7: 1, 8: 1, 9: 1 }, 20) ]; var move_BL = [ permutation_from_cycle([1, 0, 5, 6, 7], 20), unsparsify_list({ 1: 2, 0: 1, 5: 1, 6: 1, 7: 1 }, 20) ]; var move_BR = [ permutation_from_cycle([0, 4, 13, 14, 5], 20), unsparsify_list({ 0: 2, 4: 1, 13: 1, 14: 1, 5: 1 }, 20) ]; var move_x2 = [ [15, 16, 17, 18, 19, 10, 9, 8, 7, 6, 5, 14, 13, 12, 11, 0, 1, 2, 3, 4], unsparsify_list({}, 20) ]; var move_y = [ [1, 2, 3, 4, 0, 7, 8, 9, 10, 11, 12, 13, 14, 5, 6, 19, 15, 16, 17, 18], unsparsify_list({}, 20) ]; var moves = [move_U, move_R, move_F, move_L, move_BL, move_BR, move_x2]; var move_names = ["U", "R", "F", "L", "BL", "BR", "x2"]; var id = compose_o(move_x2, move_x2); var moves_full = []; for (let i = 0; i < moves.length; i++) { moves_full[i] = [id]; for (let j = 1; j < 5; j++) { moves_full[i][j] = compose_o(moves_full[i][j - 1], moves[i]); } } function random_state() { let p = [0]; for (let i = 1; i < 20; i++) { let r = randomUIntBelow(i + 1); p[i] = p[r]; p[r] = i; } if (permutation_parity(p) === 1) { [p[0], p[1]] = [p[1], p[0]]; } let o = Array(20).fill(0); for (let i = 0; i < 19; i++) { o[i] = randomUIntBelow(3); o[19] += 3 - o[i]; } o[19] %= 3; return [p, o]; } function stringify_move_sequence(move_sequence) { let suffixes = ["0", "", "2", "2'", "'"]; let s = move_sequence.map(([m, r]) => move_names[m] + suffixes[r]); return s.join(" "); } function apply_move_sequence(state, move_sequence) { for (let [m, r] of move_sequence) { for (let i = 0; i < r; i++) { state = compose_o(state, moves[m]); } } return state; } function generate_random_state_scramble() { return solve(random_state()); } function solve_phase1(state) { let p = state[0]; if (p.slice(15, 20).every((x) => x < 15)) { return []; } if (p.slice(0, 5).every((x) => x < 15)) { return [[6, 1]]; } let flags = p.map((x) => x >= 15); let depth = 0; let sol; while (sol === void 0) { depth++; sol = search_phase1(flags, depth, -1); } sol.push([6, 1]); return sol; } function search_phase1(flags, depth, last) { if (depth === 0) { if (flags.slice(0, 5).some((x) => x)) { return; } return []; } for (let move_index = 0; move_index < 6; move_index++) { if (move_index === last) { continue; } for (let r = 1; r < 5; r++) { let new_flags = compose(flags, moves_full[move_index][r][0]); let sol = search_phase1(new_flags, depth - 1, move_index); if (sol !== void 0) { return [[move_index, r]].concat(sol); } } } return; } function index_phase2(state) { let p = state[0].slice(0, 15); let o = state[1]; let index_c = comb_to_index(p.map((x) => +(x >= 15))); let index_o = 243 * index_c; for (let i = 0, j = 0; i < 15; i++) { if (p[i] < 15) { continue; } index_o += o[i] * Math.pow(3, j); j++; } let index_p = 0; for (let i = 0; i < 5; i++) { index_p += p.indexOf(15 + i) * Math.pow(15, i); } return [index_o, index_p]; } function solve_phase2(state) { let mtables = [ generate_phase23_orientation_mtable(), generate_phase23_permutation_mtable() ]; let ptables = [ generate_phase2_orientation_ptable(), generate_phase2_permutation_ptable() ]; return ida_solve(index_phase2(state), mtables, ptables).concat([[6, 1]]); } function index_phase3(state) { let pieces = [5, 6, 7, 8, 14]; let p = state[0].slice(0, 15); let o = state[1]; let index_c = comb_to_index(p.map((x) => +(pieces.indexOf(x) !== -1))); let index_o = 243 * index_c; for (let i = 0, j = 0; i < 15; i++) { if (pieces.indexOf(p[i]) === -1) { continue; } index_o += o[i] * Math.pow(3, j); j++; } let index_p = 0; for (let i = 0; i < 5; i++) { index_p += p.indexOf(pieces[i]) * Math.pow(15, i); } return [index_o, index_p]; } function solve_phase3(state) { let mtables = [ generate_phase23_orientation_mtable(), generate_phase23_permutation_mtable() ]; let ptables = [ generate_phase3_orientation_ptable(), generate_phase3_permutation_ptable() ]; return ida_solve(index_phase3(state), mtables, ptables); } function index_phase4(state) { let p = state[0].slice(0, 14); let o = state[1]; let index_o = 0; let perm = []; let j = 0; for (let i of [0, 1, 2, 3, 4, 9, 10, 11, 12, 13]) { if (i !== 13) { index_o += o[i] * Math.pow(3, j); } perm[j] = p[i] < 5 ? p[i] : p[i] - 4; j++; } return [index_o, evenpermutation_to_index(perm)]; } function solve_phase4_fast(state) { return phase4_ida_solve(index_phase4(state)); } function solve(state) { let sol = []; for (let solver of [ solve_phase1, solve_phase2, solve_phase3, solve_phase4_fast ]) { let phase_sol = solver(state); state = apply_move_sequence(state, phase_sol); sol = sol.concat(phase_sol); } return sol; } var tables = {}; function generate_phase23_orientation_mtable() { if (tables.phase23om) { return tables.phase23om; } const C15_5 = C(15, 5); const THREE = [1, 3, 9, 27, 81, 243]; let phase23om = Array(C(15, 5) * THREE[5]); tables.phase23om = phase23om; for (let i = 0; i < C15_5; i++) { let comb = index_to_comb(i, 5, 15).concat(Array(5).fill(0)); let new_comb_indices = []; for (let move_index = 0; move_index < 6; move_index++) { let new_comb = compose(comb, moves[move_index][0]).slice(0, 15); new_comb_indices[move_index] = comb_to_index(new_comb); } for (let j = 0; j < THREE[5]; j++) { phase23om[j + 243 * i] = []; let orient_full = []; for (let k = 0, l = 0; k < 20; k++) { if (comb[k] === 1) { orient_full[k] = (j / THREE[l] | 0) % 3; l++; } else { orient_full[k] = 99; } } for (let move_index = 0; move_index < 6; move_index++) { let move = moves[move_index]; let new_orient_full = []; for (let k = 0; k < 15; k++) { new_orient_full[k] = orient_full[move[0][k]] + move[1][k]; } let new_orient = new_orient_full.filter((x) => x < 10); let J = 0; for (let k = 0; k < 5; k++) { J += new_orient[k] % 3 * THREE[k]; } phase23om[j + 243 * i][move_index] = J + 243 * new_comb_indices[move_index]; } } } return phase23om; } function generate_phase2_orientation_ptable() { if (tables.phase2op) { return tables.phase2op; } let mtable = generate_phase23_orientation_mtable(); return tables.phase2op = bfs(mtable, [243 * 3002]); } function generate_phase3_orientation_ptable() { if (tables.phase3op) { return tables.phase3op; } let mtable = generate_phase23_orientation_mtable(); return tables.phase3op = bfs(mtable, [243 * 246]); } function generate_phase23_permutation_mtable() { if (tables.phase23pm) { return tables.phase23pm; } const FIFTEEN = [ 1, 15, 225, Math.pow(15, 3), Math.pow(15, 4), Math.pow(15, 5) ]; let phase23pm = Array(FIFTEEN[5]); let single = Array(15); for (let i = 0; i < 15; i++) { single[i] = Array(6); for (let move_index = 0; move_index < 6; move_index++) { single[i][move_index] = moves[move_index][0].indexOf(i); } } let locations = [0, 0, 0, 0, 0]; for (let ind = 0; ind < FIFTEEN[5]; ind++) { phase23pm[ind] = Array(6); for (let move_index = 0; move_index < 6; move_index++) { let new_ind = 0; for (let i = 0; i < 5; i++) { new_ind += single[locations[i]][move_index] * FIFTEEN[i]; } phase23pm[ind][move_index] = new_ind; } locations[0]++; for (let i = 0; i < 4; i++) { if (locations[i] === 15) { locations[i] = 0; locations[i + 1]++; } } } return tables.phase23pm = phase23pm; } function generate_phase2_permutation_ptable() { if (tables.phase2pp) { return tables.phase2pp; } let mtable = generate_phase23_permutation_mtable(); return tables.phase2pp = bfs(mtable, [213090]); } function generate_phase3_permutation_ptable() { if (tables.phase3pp) { return tables.phase3pp; } let mtable = generate_phase23_permutation_mtable(); return tables.phase3pp = bfs(mtable, [737420]); } function generate_phase4_orientation_mtable() { if (tables.phase4om) { return tables.phase4om; } const THREE = [1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049]; let mtable = Array(THREE[9]); for (let i = 0; i < THREE[9]; i++) { let o = Array(14).fill(0); for (let j = 0; j < 9; j++) { let J = j < 5 ? j : j + 4; o[J] = (i / THREE[j] | 0) % 3; o[13] -= o[J]; } o[13] = (o[13] + 999) % 3; mtable[i] = []; for (let move_index = 0; move_index < 3; move_index++) { let move = moves[move_index]; let new_o = [0, 1, 2, 3, 4, 9, 10, 11, 12, 13].map( (i2) => o[move[0][i2]] + move[1][i2] ); let new_i = 0; for (let j = 0; j < 9; j++) { new_i += new_o[j] % 3 * THREE[j]; } mtable[i][move_index] = new_i; } } return tables.phase4om = mtable; } function generate_phase4_permutation_mtable() { if (tables.phase4pm) { return tables.phase4pm; } const HALFFACT10 = factorial(10) / 2; const n = 10; let pre = [0, 1, 2, 3, 4, -1, -1, -1, -1, 5, 6, 7, 8, 9]; let post = [0, 1, 2, 3, 4, 9, 10, 11, 12, 13]; let move_permutations = [ compose(pre, compose(move_U[0], post)), compose(pre, compose(move_R[0], post)), compose(pre, compose(move_F[0], post)) ]; let mtable = Array(HALFFACT10); let perm = Array(10); for (let i = 0; i < HALFFACT10; i++) { index_to_evenpermutation10(i, perm); mtable[i] = []; for (let move_index = 0; move_index < 3; move_index++) { let new_perm = compose(perm, move_permutations[move_index]); mtable[i][move_index] = evenpermutation10_to_index(new_perm); } } return tables.phase4pm = mtable; } function generate_phase4_orientation_ptable() { if (tables.phase4op) { return tables.phase4op; } let mtable = generate_phase4_orientation_mtable(); return tables.phase4op = bfs(mtable, [0]); } function generate_phase4_permutation_ptable() { if (tables.phase4pp) { return tables.phase4pp; } let mtable = generate_phase4_permutation_mtable(); return tables.phase4pp = bfs(mtable, [0]); } function generate_phase4_near_ptable_list(threshold) { if (tables.phase4np_list && tables.phase4np_list.threshold === threshold) { return tables.phase4np_list; } let mtables = [ generate_phase4_orientation_mtable(), generate_phase4_permutation_mtable() ]; let base = Math.pow(3, 9); let states = [0]; populate(threshold, [0, 0], -1); function populate(depth, state, last2) { states.push(state[0] + base * state[1]); if (depth === 0) { return; } let new_state = []; for (let move_index = 0; move_index < 3; move_index++) { if (move_index === last2) { continue; } new_state[0] = state[0]; new_state[1] = state[1]; for (let r = 1; r < 5; r++) { new_state[0] = mtables[0][new_state[0]][move_index]; new_state[1] = mtables[1][new_state[1]][move_index]; populate(depth - 1, new_state, move_index); } } return; } states.sort((x, y) => x - y); let unique_states = []; let last = -1; for (let state of states) { if (state !== last) { unique_states.push(last = state); } } unique_states.threshold = threshold; return tables.phase4np_list = unique_states; } function binary_search(A, x) { let lo = 0; let hi = A.length - 1; while (hi - lo > 1) { let mid = lo + hi >> 1; if (x > A[mid]) { lo = mid + 1; } else { hi = mid; } } return x === A[lo] || x === A[hi]; } function bfs(mtable, goal_states) { let N = mtable.length; let nmoves = mtable[0].length; let ptable = Array(N).fill(-1); for (let state of goal_states) { ptable[state] = 0; } let depth = 0; let done = false; while (!done) { done = true; for (let state = 0; state < N; state++) { if (ptable[state] !== depth) { continue; } for (let move_index = 0; move_index < nmoves; move_index++) { let new_state = mtable[state][move_index]; while (new_state !== state) { if (ptable[new_state] === -1) { done = false; ptable[new_state] = depth + 1; } new_state = mtable[new_state][move_index]; } } } depth++; } return ptable; } function ida_solve(indices, mtables, ptables) { let ncoords = indices.length; let bound = 0; for (let i = 0; i < ncoords; i++) { bound = Math.max(bound, ptables[i][indices[i]]); } while (true) { let path = ida_search(indices, mtables, ptables, bound, -1); if (path !== void 0) { return path; } bound++; } } function ida_search(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 || heuristic === 0) { return []; } for (let m = 0; m < nmoves; m++) { if (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 = ida_search(new_indices, mtables, ptables, bound - 1, m); if (subpath !== void 0) { return [[m, r]].concat(subpath); } for (let c = 0; c < ncoords; c++) { new_indices[c] = mtables[c][new_indices[c]][m]; } r++; } } return; } function phase4_ida_solve(indices) { let mtable_o = generate_phase4_orientation_mtable(); let mtable_p = generate_phase4_permutation_mtable(); let ptable_o = generate_phase4_orientation_ptable(); let ptable_p = generate_phase4_permutation_ptable(); let ptable_n = generate_phase4_near_ptable_list(PHASE4_THRESHOLD); let bound = Math.max(ptable_o[indices[0]], ptable_p[indices[1]]); while (true) { let path = phase4_ida_search( indices, bound, -1, mtable_o, mtable_p, ptable_o, ptable_p, ptable_n ); if (path !== void 0) { return path; } bound++; } } function phase4_ida_search(indices, bound, last, mtable_o, mtable_p, ptable_o, ptable_p, ptable_n) { let heuristic = Math.max(ptable_o[indices[0]], ptable_p[indices[1]]); if (heuristic > bound) { return; } if (heuristic <= PHASE4_THRESHOLD && !binary_search(ptable_n, indices[0] + 19683 * indices[1])) { heuristic = PHASE4_THRESHOLD + 1; } if (heuristic > bound) { return; } if (bound === 0 || heuristic === 0) { return []; } for (let m = 0; m < 3; m++) { if (m === last) { continue; } let new_indices = indices.slice(); for (let r = 1; r < 5; r++) { new_indices[0] = mtable_o[new_indices[0]][m]; new_indices[1] = mtable_p[new_indices[1]][m]; let subpath = phase4_ida_search( new_indices, bound - 1, m, mtable_o, mtable_p, ptable_o, ptable_p, ptable_n ); if (subpath !== void 0) { return [[m, r]].concat(subpath); } } } return; } function getRandomKilominxScramble() { return new Alg(stringify_move_sequence(generate_random_state_scramble())); } export { getRandomKilominxScramble }; //# sourceMappingURL=search-dynamic-solve-kilominx-SAJCQILQ.js.map