cubing
Version:
A collection of JavaScript cubing libraries.
1,661 lines (1,643 loc) • 306 kB
JavaScript
import {
KPattern,
KPuzzle
} from "./chunk-RINY3U6G.js";
import {
Alg,
AlgBuilder,
Commutator,
Conjugate,
Grouping,
LineComment,
Move,
Newline,
Pause,
QuantumMove,
TraversalDownUp,
TraversalUp,
direct,
directedGenerator,
endCharIndexKey,
experimentalAppendMove,
functionFromTraversal,
offsetMod,
startCharIndexKey
} from "./chunk-O6HEZXGY.js";
// src/cubing/notation/CountAnimatedLeaves.ts
var CountAnimatedLeaves = class extends TraversalUp {
traverseAlg(alg) {
let total = 0;
for (const part of alg.childAlgNodes()) {
total += this.traverseAlgNode(part);
}
return total;
}
traverseGrouping(grouping) {
return this.traverseAlg(grouping.alg) * Math.abs(grouping.amount);
}
traverseMove(_move) {
return 1;
}
traverseCommutator(commutator) {
return 2 * (this.traverseAlg(commutator.A) + this.traverseAlg(commutator.B));
}
traverseConjugate(conjugate) {
return 2 * this.traverseAlg(conjugate.A) + this.traverseAlg(conjugate.B);
}
traversePause(_pause) {
return 1;
}
traverseNewline(_newline) {
return 0;
}
traverseLineComment(_comment) {
return 0;
}
};
var countAnimatedLeaves = functionFromTraversal(CountAnimatedLeaves);
// src/cubing/notation/commonMetrics.ts
var CommonMetric = /* @__PURE__ */ ((CommonMetric2) => {
CommonMetric2["OuterBlockTurnMetric"] = "OBTM";
CommonMetric2["RangeBlockTurnMetric"] = "RBTM";
CommonMetric2["SingleSliceTurnMetric"] = "SSTM";
CommonMetric2["OuterBlockQuantumTurnMetric"] = "OBQTM";
CommonMetric2["RangeBlockQuantumTurnMetric"] = "RBQTM";
CommonMetric2["SingleSliceQuantumTurnMetric"] = "SSQTM";
CommonMetric2["ExecutionTurnMetric"] = "ETM";
return CommonMetric2;
})(CommonMetric || {});
var CommonMetricAlias = /* @__PURE__ */ ((CommonMetricAlias2) => {
CommonMetricAlias2["QuantumTurnMetric"] = "OBQTM";
CommonMetricAlias2["HandTurnMetric"] = "OBTM";
CommonMetricAlias2["SliceTurnMetric"] = "RBTM";
return CommonMetricAlias2;
})(CommonMetricAlias || {});
// src/cubing/twisty/LazyPromise.ts
var LazyPromise = class {
#executor;
constructor(executor) {
this.#executor = executor;
}
#cached;
async #getCached() {
return this.#cached ??= Promise.resolve(this.#executor());
}
// Type signature from TypeScript
// biome-ignore lint/suspicious/noThenProperty: We're implementing the `Promise` API!
async then(onfulfilled, onrejected) {
return this.#getCached().then(onfulfilled, onrejected);
}
// Type signature from TypeScript
catch(onrejected) {
return this.#getCached().catch(onrejected);
}
// Type signature from TypeScript
async finally(onfinally) {
return this.#getCached().finally(onfinally);
}
get [Symbol.toStringTag]() {
return "LazyPromise";
}
};
// src/cubing/twisty/model/PromiseFreshener.ts
var StaleDropper = class {
#latestAssignedIdx = 0;
#latestResolvedIdx = 0;
queue(p) {
return new Promise(async (resolve, reject) => {
try {
const idx = ++this.#latestAssignedIdx;
const result = await p;
if (idx > this.#latestResolvedIdx) {
this.#latestResolvedIdx = idx;
resolve(result);
}
} catch (e) {
reject(e);
}
});
}
};
// src/cubing/twisty/model/props/TwistyProp.ts
var globalSourceGeneration = 0;
var TwistyPropParent = class {
// Don't overwrite this. Overwrite `canReuseValue` instead.
canReuse(v1, v2) {
return v1 === v2 || this.canReuseValue(v1, v2);
}
// Overwrite with a cheap semantic comparison when possible.
// Note that this is not called if `v1 === v2` (in which case the value is automatically reused).
canReuseValue(_v1, _v2) {
return false;
}
debugGetChildren() {
return Array.from(this.#children.values());
}
// Propagation
#children = /* @__PURE__ */ new Set();
addChild(child) {
this.#children.add(child);
}
removeChild(child) {
this.#children.delete(child);
}
lastSourceGeneration = 0;
// Synchronously marks all descendants as stale. This doesn't actually
// literally mark as stale, but it updates the last source generation, which
// is used to tell if a cahced result is stale.
markStale(sourceEvent) {
if (sourceEvent.detail.generation !== globalSourceGeneration) {
throw new Error("A TwistyProp was marked stale too late!");
}
if (this.lastSourceGeneration === sourceEvent.detail.generation) {
return;
}
this.lastSourceGeneration = sourceEvent.detail.generation;
for (const child of this.#children) {
child.markStale(sourceEvent);
}
this.#scheduleRawDispatch();
}
#rawListeners = /* @__PURE__ */ new Set();
/** @deprecated */
addRawListener(listener, options) {
this.#rawListeners.add(listener);
if (options?.initial) {
listener();
}
}
/** @deprecated */
removeRawListener(listener) {
this.#rawListeners.delete(listener);
}
/** @deprecated */
#scheduleRawDispatch() {
if (!this.#rawDispatchPending) {
this.#rawDispatchPending = true;
setTimeout(() => this.#dispatchRawListeners(), 0);
}
}
#rawDispatchPending = false;
#dispatchRawListeners() {
if (!this.#rawDispatchPending) {
throw new Error("Invalid dispatch state!");
}
for (const listener of this.#rawListeners) {
listener();
}
this.#rawDispatchPending = false;
}
#freshListeners = /* @__PURE__ */ new Map();
// TODO: Pick a better name.
addFreshListener(listener) {
const staleDropper = new StaleDropper();
let lastResult = null;
const callback = async () => {
const result = await staleDropper.queue(this.get());
if (lastResult !== null && this.canReuse(lastResult, result)) {
return;
}
lastResult = result;
listener(result);
};
this.#freshListeners.set(listener, callback);
this.addRawListener(callback, { initial: true });
}
removeFreshListener(listener) {
this.removeRawListener(this.#freshListeners.get(listener));
this.#freshListeners.delete(listener);
}
};
var TwistyPropSource = class extends TwistyPropParent {
#value;
constructor(initialValue) {
super();
this.#value = new LazyPromise(async () => this.getDefaultValue());
if (initialValue) {
this.#value = this.deriveFromPromiseOrValue(initialValue, this.#value);
}
}
set(input) {
this.#value = this.deriveFromPromiseOrValue(input, this.#value);
const sourceEventDetail = {
sourceProp: this,
value: this.#value,
generation: ++globalSourceGeneration
};
this.markStale(
new CustomEvent("stale", {
detail: sourceEventDetail
})
);
}
async get() {
return this.#value;
}
async deriveFromPromiseOrValue(input, oldValuePromise) {
return this.derive(await input, oldValuePromise);
}
};
var SimpleTwistyPropSource = class extends TwistyPropSource {
derive(input) {
return input;
}
};
var NO_VALUE = Symbol("no value");
var TwistyPropDerived = class extends TwistyPropParent {
constructor(parents, userVisibleErrorTracker) {
super();
this.userVisibleErrorTracker = userVisibleErrorTracker;
this.#parents = parents;
for (const parent of Object.values(parents)) {
parent.addChild(this);
}
}
// cachedInputs:
#parents;
#cachedLastSuccessfulCalculation = null;
#cachedLatestGenerationCalculation = null;
async get() {
const generation = this.lastSourceGeneration;
if (this.#cachedLatestGenerationCalculation?.generation === generation) {
return this.#cachedLatestGenerationCalculation.output;
}
const latestGenerationCalculation = {
generation,
output: this.#cacheDerive(
this.#getParents(),
generation,
this.#cachedLastSuccessfulCalculation
)
};
this.#cachedLatestGenerationCalculation = latestGenerationCalculation;
this.userVisibleErrorTracker?.reset();
return latestGenerationCalculation.output;
}
async #getParents() {
const inputValuePromises = {};
for (const [key, parent] of Object.entries(this.#parents)) {
inputValuePromises[key] = parent.get();
}
const inputs = {};
for (const key in this.#parents) {
inputs[key] = await inputValuePromises[key];
}
return inputs;
}
async #cacheDerive(inputsPromise, generation, cachedLatestGenerationCalculation = null) {
const inputs = await inputsPromise;
const cache2 = (output) => {
this.#cachedLastSuccessfulCalculation = {
inputs,
output: Promise.resolve(output),
generation
};
return output;
};
if (!cachedLatestGenerationCalculation) {
return cache2(await this.derive(inputs));
}
const cachedInputs = cachedLatestGenerationCalculation.inputs;
for (const key in this.#parents) {
const parent = this.#parents[key];
if (!parent.canReuse(inputs[key], cachedInputs[key])) {
return cache2(await this.derive(inputs));
}
}
return cachedLatestGenerationCalculation.output;
}
};
var FreshListenerManager = class {
#disconnectionFunctions = [];
addListener(prop, listener) {
let disconnected = false;
const wrappedListener = (value) => {
if (disconnected) {
return;
}
listener(value);
};
prop.addFreshListener(wrappedListener);
this.#disconnectionFunctions.push(() => {
prop.removeFreshListener(wrappedListener);
disconnected = true;
});
}
// TODO: Figure out the signature to let us do overloads
/** @deprecated */
addMultiListener3(props, listener) {
this.addMultiListener(props, listener);
}
addMultiListener(props, listener) {
let disconnected = false;
let initialIgnoresLeft = props.length - 1;
const wrappedListener = async (_) => {
if (initialIgnoresLeft > 0) {
initialIgnoresLeft--;
return;
}
if (disconnected) {
return;
}
const promises = props.map(
(prop) => prop.get()
);
const values = await Promise.all(promises);
listener(values);
};
for (const prop of props) {
prop.addFreshListener(wrappedListener);
}
this.#disconnectionFunctions.push(() => {
for (const prop of props) {
prop.removeFreshListener(wrappedListener);
}
disconnected = true;
});
}
disconnect() {
for (const disconnectionFunction of this.#disconnectionFunctions) {
disconnectionFunction();
}
}
};
// src/cubing/twisty/model/props/puzzle/display/StickeringRequestProp.ts
var StickeringRequestProp = class extends SimpleTwistyPropSource {
getDefaultValue() {
return null;
}
};
// src/cubing/twisty/views/node-custom-element-shims.ts
var HTMLElementStub = class {
};
var HTMLElementShim;
if (globalThis.HTMLElement) {
HTMLElementShim = globalThis.HTMLElement;
} else {
HTMLElementShim = HTMLElementStub;
}
var CustomElementsStub = class {
define() {
}
};
var customElementsShim;
if (globalThis.customElements) {
customElementsShim = globalThis.customElements;
} else {
customElementsShim = new CustomElementsStub();
}
var cssStyleSheetShim;
var CSSStyleSheetStub = class {
replaceSync() {
}
};
if (globalThis.CSSStyleSheet) {
cssStyleSheetShim = globalThis.CSSStyleSheet;
} else {
cssStyleSheetShim = CSSStyleSheetStub;
}
// src/cubing/twisty/views/ManagedCustomElement.ts
var ManagedCustomElement = class extends HTMLElementShim {
shadow;
// TODO: hide this
contentWrapper;
// TODO: can we get rid of this wrapper?
constructor(options) {
super();
this.shadow = this.attachShadow({ mode: options?.mode ?? "closed" });
this.contentWrapper = document.createElement("div");
this.contentWrapper.classList.add("wrapper");
this.shadow.appendChild(this.contentWrapper);
}
// Add the source, if not already added.
// Returns the existing if it's already on the element.
addCSS(cssSource) {
this.shadow.adoptedStyleSheets.push(cssSource);
}
removeCSS(cssSource) {
const cssIndex = this.shadow.adoptedStyleSheets.indexOf(cssSource);
if (typeof cssIndex !== "undefined") {
this.shadow.adoptedStyleSheets.splice(cssIndex, cssIndex + 1);
}
}
addElement(element) {
return this.contentWrapper.appendChild(element);
}
prependElement(element) {
this.contentWrapper.prepend(element);
}
removeElement(element) {
return this.contentWrapper.removeChild(element);
}
};
customElementsShim.define(
"twisty-managed-custom-element",
ManagedCustomElement
);
// src/cubing/twisty/views/stream/TwistyStreamSource.css.ts
var twistyStreamSourceCSS = new cssStyleSheetShim();
twistyStreamSourceCSS.replaceSync(
`
:host {
width: 384px;
height: 256px;
display: grid;
font-family: "Ubuntu", sans-serif;
}
.wrapper {
display: grid;
place-content: center;
gap: 0.5em;
}
`
);
// src/cubing/twisty/views/stream/TwistyStreamSource.ts
var BluetoothStreamSource = class _BluetoothStreamSource extends EventTarget {
constructor(puzzle) {
super();
this.puzzle = puzzle;
puzzle.addAlgLeafListener((e) => {
const move = e.latestAlgLeaf.as(Move);
if (!move) {
return;
}
this.dispatchEvent(
new CustomEvent("move", {
detail: {
move
}
})
);
});
}
static async connect() {
const bluetooth = await import("../bluetooth/index.js");
const puzzle = await bluetooth.connectSmartPuzzle();
return new _BluetoothStreamSource(puzzle);
}
disconnect() {
this.puzzle.disconnect();
}
};
var KeyboardStreamSource = class _KeyboardStreamSource extends EventTarget {
constructor(puzzle) {
super();
this.puzzle = puzzle;
puzzle.addAlgLeafListener((e) => {
const move = e.latestAlgLeaf.as(Move);
if (!move) {
return;
}
this.dispatchEvent(
new CustomEvent("move", {
detail: {
move
}
})
);
});
}
static async connect() {
const bluetooth = await import("../bluetooth/index.js");
const puzzle = await bluetooth.debugKeyboardConnect();
return new _KeyboardStreamSource(puzzle);
}
disconnect() {
this.puzzle.disconnect();
}
};
var TwistyStreamSource = class extends ManagedCustomElement {
constructor() {
super();
this.addCSS(twistyStreamSourceCSS);
this.addElement(document.createElement("span")).textContent = "Connect a stream source:";
const bluetoothButton = this.addSource(
"\u{1F4E1} Bluetooth",
BluetoothStreamSource
);
this.addSource("\u2328\uFE0F Keyboard", KeyboardStreamSource);
this.addStreamSource();
if (!navigator?.bluetooth) {
bluetoothButton.disabled = true;
}
}
addSource(label, sourceClass) {
const button = this.addElement(document.createElement("button"));
button.textContent = label;
button.addEventListener("click", async () => {
const source = await sourceClass.connect();
button.disabled = true;
button.textContent += " \u2705";
source.addEventListener(
"move",
((e) => {
this.dispatchEvent(new CustomEvent("move", e));
})
// TODO: https://github.com/microsoft/TypeScript/issues/28357
);
});
return button;
}
addStreamSource() {
const SENTINEL_VALUE = "SENTINEL";
const button = this.addElement(document.createElement("button"));
button.textContent = "\u{1F534} Get Twizzle streams";
const select = this.addElement(document.createElement("select"));
select.appendChild(document.createElement("option")).textContent = "Streams";
select.disabled = true;
let streamServer = null;
button.addEventListener("click", async () => {
const TwizzleStreamServer = (await import("../stream/index.js")).ExperimentalTwizzleStreamServer;
streamServer ||= new TwizzleStreamServer();
const streams = await streamServer.streams();
select.textContent = "";
select.disabled = false;
const info = select.appendChild(document.createElement("option"));
info.textContent = `Select a stream (${streams.length} available)`;
info.value = SENTINEL_VALUE;
for (const stream of streams) {
const firstSender = stream.senders[0];
const option = select.appendChild(document.createElement("option"));
option.value = stream.streamID;
option.textContent = `${firstSender.name} (${stream.streamID.slice(
-2
)})`;
}
});
select.addEventListener("change", () => {
const streamID = select.value;
if (streamID === SENTINEL_VALUE) {
return;
}
const stream = streamServer.connect(streamID);
stream.addEventListener(
"move",
((moveEvent) => {
console.log(moveEvent);
this.dispatchEvent(new CustomEvent("move", moveEvent));
})
// TODO: https://github.com/microsoft/TypeScript/issues/28357
);
});
}
};
customElementsShim.define("twisty-stream-source", TwistyStreamSource);
// src/cubing/puzzles/events.ts
var wcaEvents = {
"333": {
puzzleID: "3x3x3",
eventName: "3x3x3 Cube",
scramblesImplemented: "random-state"
},
"222": {
puzzleID: "2x2x2",
eventName: "2x2x2 Cube",
scramblesImplemented: "random-state"
},
"444": {
puzzleID: "4x4x4",
eventName: "4x4x4 Cube",
scramblesImplemented: "random-state"
},
"555": {
puzzleID: "5x5x5",
eventName: "5x5x5 Cube",
scramblesImplemented: "random-moves"
},
"666": {
puzzleID: "6x6x6",
eventName: "6x6x6 Cube",
scramblesImplemented: "random-moves"
},
"777": {
puzzleID: "7x7x7",
eventName: "7x7x7 Cube",
scramblesImplemented: "random-moves"
},
"333bf": {
puzzleID: "3x3x3",
eventName: "3x3x3 Blindfolded",
scramblesImplemented: "random-state"
},
"333fm": {
puzzleID: "3x3x3",
eventName: "3x3x3 Fewest Moves",
scramblesImplemented: "random-state"
},
"333oh": {
puzzleID: "3x3x3",
eventName: "3x3x3 One-Handed",
scramblesImplemented: "random-state"
},
clock: {
puzzleID: "clock",
eventName: "Clock",
scramblesImplemented: "random-state"
},
minx: {
puzzleID: "megaminx",
eventName: "Megaminx",
scramblesImplemented: "random-moves"
},
pyram: {
puzzleID: "pyraminx",
eventName: "Pyraminx",
scramblesImplemented: "random-state"
},
skewb: {
puzzleID: "skewb",
eventName: "Skewb",
scramblesImplemented: "random-state"
},
sq1: {
puzzleID: "square1",
eventName: "Square-1",
scramblesImplemented: "random-state"
},
"444bf": {
puzzleID: "4x4x4",
eventName: "4x4x4 Blindfolded",
scramblesImplemented: "random-state"
},
"555bf": {
puzzleID: "5x5x5",
eventName: "5x5x5 Blindfolded",
scramblesImplemented: "random-moves"
},
"333mbf": {
puzzleID: "3x3x3",
eventName: "3x3x3 Multi-Blind",
scramblesImplemented: "random-state"
}
};
function wcaEventInfo(event) {
return wcaEvents[event] ?? null;
}
var twizzleEvents = {
...wcaEvents,
fto: {
puzzleID: "fto",
eventName: "Face-Turning Octahedron",
scramblesImplemented: "random-state"
},
master_tetraminx: {
puzzleID: "master_tetraminx",
eventName: "Master Tetraminx",
scramblesImplemented: "random-state"
},
kilominx: {
puzzleID: "kilominx",
eventName: "Kilominx",
scramblesImplemented: "random-state"
},
redi_cube: {
puzzleID: "redi_cube",
eventName: "Redi Cube",
scramblesImplemented: "random-state"
},
baby_fto: {
puzzleID: "baby_fto",
eventName: "Baby FTO",
scramblesImplemented: "random-state"
},
loopover: {
puzzleID: "loopover",
eventName: "Loopover",
scramblesImplemented: null
}
};
function eventInfo(event) {
return twizzleEvents[event] ?? null;
}
// src/cubing/puzzles/stickerings/mask.ts
function getFaceletStickeringMask(stickeringMask, orbitName, pieceIdx, faceletIdx, hint) {
const orbitStickeringMask = stickeringMask.orbits[orbitName];
const pieceStickeringMask = orbitStickeringMask.pieces[pieceIdx];
if (pieceStickeringMask === null) {
return regular;
}
const faceletStickeringMask = pieceStickeringMask.facelets?.[faceletIdx];
if (faceletStickeringMask === null) {
return regular;
}
if (typeof faceletStickeringMask === "string") {
return faceletStickeringMask;
}
if (hint) {
return faceletStickeringMask.hintMask ?? faceletStickeringMask.mask;
}
console.log(faceletStickeringMask);
return faceletStickeringMask.mask;
}
var PieceAnnotation = class {
stickerings = /* @__PURE__ */ new Map();
constructor(kpuzzle, defaultValue) {
for (const orbitDefinition of kpuzzle.definition.orbits) {
this.stickerings.set(
orbitDefinition.orbitName,
new Array(orbitDefinition.numPieces).fill(defaultValue)
);
}
}
};
var regular = "regular";
var ignored = "ignored";
var oriented = "oriented";
var experimentalOriented2 = "experimentalOriented2";
var invisible = "invisible";
var dim = "dim";
var mystery = "mystery";
var pieceStickerings = {
// regular
["Regular" /* Regular */]: {
// r
facelets: [regular, regular, regular, regular, regular]
},
// ignored
["Ignored" /* Ignored */]: {
// i
facelets: [ignored, ignored, ignored, ignored, ignored]
},
// oriented stickers
["OrientationStickers" /* OrientationStickers */]: {
// o
facelets: [oriented, oriented, oriented, oriented, oriented]
},
// "OLL"
["IgnoreNonPrimary" /* IgnoreNonPrimary */]: {
// riiii
facelets: [regular, ignored, ignored, ignored, ignored]
},
// invisible
["Invisible" /* Invisible */]: {
// invisiblePiece
facelets: [invisible, invisible, invisible, invisible, invisible]
},
// "PLL"
["PermuteNonPrimary" /* PermuteNonPrimary */]: {
// drrrr
facelets: [dim, regular, regular, regular, regular]
},
// ignored
["Dim" /* Dim */]: {
// d
facelets: [dim, dim, dim, dim, dim]
},
// "OLL"
["Ignoriented" /* Ignoriented */]: {
// diiii
facelets: [dim, ignored, ignored, ignored, ignored]
},
["OrientationWithoutPermutation" /* OrientationWithoutPermutation */]: {
// oiiii
facelets: [oriented, ignored, ignored, ignored, ignored]
},
["ExperimentalOrientationWithoutPermutation2" /* ExperimentalOrientationWithoutPermutation2 */]: {
// oiiii
facelets: [experimentalOriented2, ignored, ignored, ignored, ignored]
},
["Mystery" /* Mystery */]: {
// oiiii
facelets: [mystery, mystery, mystery, mystery, mystery]
}
};
function getPieceStickeringMask(pieceStickering) {
return pieceStickerings[pieceStickering];
}
var PuzzleStickering = class extends PieceAnnotation {
constructor(kpuzzle) {
super(kpuzzle, "Regular" /* Regular */);
}
set(pieceSet, pieceStickering) {
for (const [orbitName, pieces] of this.stickerings.entries()) {
for (let i = 0; i < pieces.length; i++) {
if (pieceSet.stickerings.get(orbitName)[i]) {
pieces[i] = pieceStickering;
}
}
}
return this;
}
toStickeringMask() {
const stickeringMask = { orbits: {} };
for (const [orbitName, pieceStickerings2] of this.stickerings.entries()) {
const pieces = [];
const orbitStickeringMask = {
pieces
};
stickeringMask.orbits[orbitName] = orbitStickeringMask;
for (const pieceStickering of pieceStickerings2) {
pieces.push(getPieceStickeringMask(pieceStickering));
}
}
return stickeringMask;
}
};
var StickeringManager = class {
constructor(kpuzzle) {
this.kpuzzle = kpuzzle;
}
and(pieceSets) {
const newPieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitDefinition of this.kpuzzle.definition.orbits) {
pieceLoop: for (let i = 0; i < orbitDefinition.numPieces; i++) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = true;
for (const pieceSet of pieceSets) {
if (!pieceSet.stickerings.get(orbitDefinition.orbitName)[i]) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = false;
continue pieceLoop;
}
}
}
}
return newPieceSet;
}
or(pieceSets) {
const newPieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitDefinition of this.kpuzzle.definition.orbits) {
pieceLoop: for (let i = 0; i < orbitDefinition.numPieces; i++) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = false;
for (const pieceSet of pieceSets) {
if (pieceSet.stickerings.get(orbitDefinition.orbitName)[i]) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = true;
continue pieceLoop;
}
}
}
}
return newPieceSet;
}
not(pieceSet) {
const newPieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitDefinition of this.kpuzzle.definition.orbits) {
for (let i = 0; i < orbitDefinition.numPieces; i++) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = !pieceSet.stickerings.get(orbitDefinition.orbitName)[i];
}
}
return newPieceSet;
}
all() {
return this.and(this.moves([]));
}
move(moveSource) {
const transformation = this.kpuzzle.moveToTransformation(moveSource);
const newPieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitDefinition of this.kpuzzle.definition.orbits) {
for (let i = 0; i < orbitDefinition.numPieces; i++) {
if (transformation.transformationData[orbitDefinition.orbitName].permutation[i] !== i || transformation.transformationData[orbitDefinition.orbitName].orientationDelta[i] !== 0) {
newPieceSet.stickerings.get(orbitDefinition.orbitName)[i] = true;
}
}
}
return newPieceSet;
}
moves(moveSources) {
return moveSources.map((moveSource) => this.move(moveSource));
}
orbits(orbitNames) {
const pieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitName of orbitNames) {
pieceSet.stickerings.get(orbitName).fill(true);
}
return pieceSet;
}
orbitPrefix(orbitPrefix) {
const pieceSet = new PieceAnnotation(this.kpuzzle, false);
for (const orbitDefinition of this.kpuzzle.definition.orbits) {
if (orbitDefinition.orbitName.startsWith(orbitPrefix)) {
pieceSet.stickerings.get(orbitDefinition.orbitName).fill(true);
}
}
return pieceSet;
}
// trueCounts(pieceSet: PieceSet): Record<string, number> {
// const counts: Record<string, number> = {};
// for (const orbitDefinition of this.def.orbits) {
// let count = 0;
// for (let i = 0; i < orbitDefinition.numPieces; i++) {
// if (pieceSet.stickerings.get(orbitDefinition.orbitName)![i]) {
// count++;
// }
// }
// counts[orbitName] = count;
// }
// return counts;
// }
};
// src/cubing/puzzles/stickerings/puzzle-stickerings.ts
var LL = "Last Layer";
var LS = "Last Slot";
var megaAnd3x3x3LL = {
"3x3x3": LL,
megaminx: LL
};
var megaAnd3x3x3LS = {
"3x3x3": LS,
megaminx: LS
};
var experimentalStickerings = {
full: { groups: { "3x3x3": "Stickering", megaminx: "Stickering" } },
// default
OLL: { groups: megaAnd3x3x3LL },
PLL: { groups: megaAnd3x3x3LL },
LL: { groups: megaAnd3x3x3LL },
EOLL: { groups: megaAnd3x3x3LL },
COLL: { groups: megaAnd3x3x3LL },
OCLL: { groups: megaAnd3x3x3LL },
CPLL: { groups: megaAnd3x3x3LL },
CLL: { groups: megaAnd3x3x3LL },
EPLL: { groups: megaAnd3x3x3LL },
ELL: { groups: megaAnd3x3x3LL },
ZBLL: { groups: megaAnd3x3x3LL },
LS: { groups: megaAnd3x3x3LS },
LSOLL: { groups: megaAnd3x3x3LS },
LSOCLL: { groups: megaAnd3x3x3LS },
ELS: { groups: megaAnd3x3x3LS },
CLS: { groups: megaAnd3x3x3LS },
ZBLS: { groups: megaAnd3x3x3LS },
VLS: { groups: megaAnd3x3x3LS },
WVLS: { groups: megaAnd3x3x3LS },
F2L: { groups: { "3x3x3": "CFOP (Fridrich)" } },
Daisy: { groups: { "3x3x3": "CFOP (Fridrich)" } },
Cross: { groups: { "3x3x3": "CFOP (Fridrich)" } },
EO: { groups: { "3x3x3": "ZZ" } },
EOline: { groups: { "3x3x3": "ZZ" } },
EOcross: { groups: { "3x3x3": "ZZ" } },
FirstBlock: { groups: { "3x3x3": "Roux" } },
SecondBlock: { groups: { "3x3x3": "Roux" } },
CMLL: { groups: { "3x3x3": "Roux" } },
L10P: { groups: { "3x3x3": "Roux" } },
L6E: { groups: { "3x3x3": "Roux" } },
L6EO: { groups: { "3x3x3": "Roux" } },
"2x2x2": { groups: { "3x3x3": "Petrus" } },
"2x2x3": { groups: { "3x3x3": "Petrus" } },
EODF: { groups: { "3x3x3": "Nautilus" } },
G1: { groups: { "3x3x3": "FMC" } },
L2C: {
groups: {
"4x4x4": "Reduction",
"5x5x5": "Reduction",
"6x6x6": "Reduction"
}
},
OBL: { groups: { "2x2x2": "General" } },
PBL: {
groups: {
"2x2x2": "Ortega"
}
},
"Void Cube": { groups: { "3x3x3": "Miscellaneous" } },
invisible: { groups: { "3x3x3": "Miscellaneous" } },
picture: { groups: { "3x3x3": "Miscellaneous" } },
"centers-only": { groups: { "3x3x3": "Miscellaneous" } },
// TODO
"opposite-centers": { groups: { "4x4x4": "Reduction" } },
// TODO
"experimental-centers-U": {},
"experimental-centers-U-D": {},
"experimental-centers-U-L-D": {},
"experimental-centers-U-L-B-D": {},
"experimental-centers": {},
"experimental-fto-fc": { groups: { fto: "Bencisco" } },
"experimental-fto-f2t": { groups: { fto: "Bencisco" } },
"experimental-fto-sc": { groups: { fto: "Bencisco" } },
"experimental-fto-l2c": { groups: { fto: "Bencisco" } },
"experimental-fto-lbt": { groups: { fto: "Bencisco" } },
"experimental-fto-l3t": { groups: { fto: "Bencisco" } }
};
// src/cubing/puzzles/stickerings/cube-like-stickerings.ts
async function cubeLikeStickeringMask(puzzleLoader, stickering) {
return (await cubeLikePuzzleStickering(puzzleLoader, stickering)).toStickeringMask();
}
async function cubeLikePuzzleStickering(puzzleLoader, stickering) {
const kpuzzle = await puzzleLoader.kpuzzle();
const puzzleStickering = new PuzzleStickering(kpuzzle);
const m = new StickeringManager(kpuzzle);
const LL2 = () => m.move("U");
const orUD = () => m.or(m.moves(["U", "D"]));
const orLR = () => m.or(m.moves(["L", "R"]));
const M = () => m.not(orLR());
const F2L = () => m.not(LL2());
const CENTERS = () => m.orbitPrefix("CENTER");
const CENTER = (faceMove) => m.and([m.move(faceMove), CENTERS()]);
const EDGES = () => m.orbitPrefix("EDGE");
const EDGE = (faceMoves) => m.and([m.and(m.moves(faceMoves)), EDGES()]);
const CORNERS = () => m.or([
m.orbitPrefix("CORNER"),
m.orbitPrefix("C4RNER"),
m.orbitPrefix("C5RNER")
]);
const L6E = () => m.or([M(), m.and([LL2(), EDGES()])]);
const centerLL = () => m.and([LL2(), CENTERS()]);
const edgeFR = () => m.and([m.and(m.moves(["F", "R"])), EDGES()]);
const cornerDFR = () => m.and([m.and(m.moves(["F", "R"])), CORNERS(), m.not(LL2())]);
const slotFR = () => m.or([cornerDFR(), edgeFR()]);
function dimF2L() {
puzzleStickering.set(F2L(), "Dim" /* Dim */);
}
function setPLL() {
puzzleStickering.set(LL2(), "PermuteNonPrimary" /* PermuteNonPrimary */);
puzzleStickering.set(centerLL(), "Dim" /* Dim */);
}
function setOLL() {
puzzleStickering.set(LL2(), "IgnoreNonPrimary" /* IgnoreNonPrimary */);
puzzleStickering.set(centerLL(), "Regular" /* Regular */);
}
function dimOLL() {
puzzleStickering.set(LL2(), "Ignoriented" /* Ignoriented */);
puzzleStickering.set(centerLL(), "Dim" /* Dim */);
}
switch (stickering) {
case "full":
break;
case "PLL": {
dimF2L();
setPLL();
break;
}
case "CLS": {
dimF2L();
puzzleStickering.set(cornerDFR(), "Regular" /* Regular */);
puzzleStickering.set(LL2(), "Ignoriented" /* Ignoriented */);
puzzleStickering.set(m.and([LL2(), CENTERS()]), "Dim" /* Dim */);
puzzleStickering.set(
m.and([LL2(), CORNERS()]),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
break;
}
case "OLL": {
dimF2L();
setOLL();
break;
}
case "EOLL": {
dimF2L();
setOLL();
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Ignored" /* Ignored */);
break;
}
case "COLL": {
dimF2L();
puzzleStickering.set(m.and([LL2(), EDGES()]), "Ignoriented" /* Ignoriented */);
puzzleStickering.set(m.and([LL2(), CENTERS()]), "Dim" /* Dim */);
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Regular" /* Regular */);
break;
}
case "OCLL": {
dimF2L();
dimOLL();
puzzleStickering.set(
m.and([LL2(), CORNERS()]),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
break;
}
case "CPLL": {
dimF2L();
puzzleStickering.set(
m.and([CORNERS(), LL2()]),
"PermuteNonPrimary" /* PermuteNonPrimary */
);
puzzleStickering.set(
m.and([m.not(CORNERS()), LL2()]),
"Dim" /* Dim */
);
break;
}
case "CLL": {
dimF2L();
puzzleStickering.set(
m.not(m.and([CORNERS(), LL2()])),
"Dim" /* Dim */
);
break;
}
case "EPLL": {
dimF2L();
puzzleStickering.set(LL2(), "Dim" /* Dim */);
puzzleStickering.set(
m.and([LL2(), EDGES()]),
"PermuteNonPrimary" /* PermuteNonPrimary */
);
break;
}
case "ELL": {
dimF2L();
puzzleStickering.set(LL2(), "Dim" /* Dim */);
puzzleStickering.set(m.and([LL2(), EDGES()]), "Regular" /* Regular */);
break;
}
case "ELS": {
dimF2L();
setOLL();
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Ignored" /* Ignored */);
puzzleStickering.set(edgeFR(), "Regular" /* Regular */);
puzzleStickering.set(cornerDFR(), "Ignored" /* Ignored */);
break;
}
case "LL": {
dimF2L();
break;
}
case "F2L": {
puzzleStickering.set(LL2(), "Ignored" /* Ignored */);
break;
}
case "ZBLL": {
dimF2L();
puzzleStickering.set(LL2(), "PermuteNonPrimary" /* PermuteNonPrimary */);
puzzleStickering.set(centerLL(), "Dim" /* Dim */);
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Regular" /* Regular */);
break;
}
case "ZBLS": {
dimF2L();
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
setOLL();
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Ignored" /* Ignored */);
break;
}
case "VLS": {
dimF2L();
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
setOLL();
break;
}
case "WVLS": {
dimF2L();
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
puzzleStickering.set(m.and([LL2(), EDGES()]), "Ignoriented" /* Ignoriented */);
puzzleStickering.set(m.and([LL2(), CENTERS()]), "Dim" /* Dim */);
puzzleStickering.set(
m.and([LL2(), CORNERS()]),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
break;
}
case "LS": {
dimF2L();
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
puzzleStickering.set(LL2(), "Ignored" /* Ignored */);
puzzleStickering.set(centerLL(), "Dim" /* Dim */);
break;
}
case "LSOLL": {
dimF2L();
setOLL();
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
break;
}
case "LSOCLL": {
dimF2L();
dimOLL();
puzzleStickering.set(
m.and([LL2(), CORNERS()]),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
puzzleStickering.set(slotFR(), "Regular" /* Regular */);
break;
}
case "EO": {
puzzleStickering.set(CORNERS(), "Ignored" /* Ignored */);
puzzleStickering.set(
EDGES(),
"OrientationWithoutPermutation" /* OrientationWithoutPermutation */
);
break;
}
case "EOline": {
puzzleStickering.set(CORNERS(), "Ignored" /* Ignored */);
puzzleStickering.set(
EDGES(),
"OrientationWithoutPermutation" /* OrientationWithoutPermutation */
);
puzzleStickering.set(m.and(m.moves(["D", "M"])), "Regular" /* Regular */);
break;
}
case "EOcross": {
puzzleStickering.set(
EDGES(),
"OrientationWithoutPermutation" /* OrientationWithoutPermutation */
);
puzzleStickering.set(m.move("D"), "Regular" /* Regular */);
puzzleStickering.set(CORNERS(), "Ignored" /* Ignored */);
break;
}
case "CMLL": {
puzzleStickering.set(F2L(), "Dim" /* Dim */);
puzzleStickering.set(L6E(), "Ignored" /* Ignored */);
puzzleStickering.set(m.and([LL2(), CORNERS()]), "Regular" /* Regular */);
break;
}
case "L10P": {
puzzleStickering.set(m.not(L6E()), "Dim" /* Dim */);
puzzleStickering.set(m.and([CORNERS(), LL2()]), "Regular" /* Regular */);
break;
}
case "L6E": {
puzzleStickering.set(m.not(L6E()), "Dim" /* Dim */);
break;
}
case "L6EO": {
puzzleStickering.set(m.not(L6E()), "Dim" /* Dim */);
puzzleStickering.set(
L6E(),
"ExperimentalOrientationWithoutPermutation2" /* ExperimentalOrientationWithoutPermutation2 */
);
puzzleStickering.set(
m.and([CENTERS(), orUD()]),
"ExperimentalOrientationWithoutPermutation2" /* ExperimentalOrientationWithoutPermutation2 */
);
puzzleStickering.set(
m.and([m.move("M"), m.move("E")]),
"Ignored" /* Ignored */
);
break;
}
case "Daisy": {
puzzleStickering.set(m.all(), "Ignored" /* Ignored */);
puzzleStickering.set(CENTERS(), "Dim" /* Dim */);
puzzleStickering.set(
m.and([m.move("D"), CENTERS()]),
"Regular" /* Regular */
);
puzzleStickering.set(
m.and([m.move("U"), EDGES()]),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
break;
}
case "Cross": {
puzzleStickering.set(m.all(), "Ignored" /* Ignored */);
puzzleStickering.set(CENTERS(), "Dim" /* Dim */);
puzzleStickering.set(
m.and([m.move("D"), CENTERS()]),
"Regular" /* Regular */
);
puzzleStickering.set(
m.and([m.move("D"), EDGES()]),
"Regular" /* Regular */
);
break;
}
case "2x2x2": {
puzzleStickering.set(
m.or(m.moves(["U", "F", "R"])),
"Ignored" /* Ignored */
);
puzzleStickering.set(
m.and([m.or(m.moves(["U", "F", "R"])), CENTERS()]),
"Dim" /* Dim */
);
break;
}
case "2x2x3": {
puzzleStickering.set(m.all(), "Dim" /* Dim */);
puzzleStickering.set(
m.or(m.moves(["U", "F", "R"])),
"Ignored" /* Ignored */
);
puzzleStickering.set(
m.and([m.or(m.moves(["U", "F", "R"])), CENTERS()]),
"Dim" /* Dim */
);
puzzleStickering.set(
m.and([m.move("F"), m.not(m.or(m.moves(["U", "R"])))]),
"Regular" /* Regular */
);
break;
}
case "G1": {
puzzleStickering.set(
m.all(),
"ExperimentalOrientationWithoutPermutation2" /* ExperimentalOrientationWithoutPermutation2 */
);
puzzleStickering.set(
m.or(m.moves(["E"])),
"OrientationWithoutPermutation" /* OrientationWithoutPermutation */
);
puzzleStickering.set(m.and(m.moves(["E", "S"])), "Ignored" /* Ignored */);
break;
}
case "L2C": {
puzzleStickering.set(
m.or(m.moves(["L", "R", "B", "D"])),
"Dim" /* Dim */
);
puzzleStickering.set(m.not(CENTERS()), "Ignored" /* Ignored */);
break;
}
case "PBL": {
puzzleStickering.set(m.all(), "Ignored" /* Ignored */);
puzzleStickering.set(
m.or(m.moves(["U", "D"])),
"PermuteNonPrimary" /* PermuteNonPrimary */
);
break;
}
case "FirstBlock": {
puzzleStickering.set(
m.not(m.and([m.and(m.moves(["L"])), m.not(LL2())])),
"Ignored" /* Ignored */
);
puzzleStickering.set(CENTER("R"), "Dim" /* Dim */);
break;
}
case "SecondBlock": {
puzzleStickering.set(
m.not(m.and([m.and(m.moves(["L"])), m.not(LL2())])),
"Ignored" /* Ignored */
);
puzzleStickering.set(
m.and([m.and(m.moves(["L"])), m.not(LL2())]),
"Dim" /* Dim */
);
puzzleStickering.set(
m.and([m.and(m.moves(["R"])), m.not(LL2())]),
"Regular" /* Regular */
);
break;
}
case "EODF": {
dimF2L();
puzzleStickering.set(
m.or([cornerDFR(), m.and([LL2(), CORNERS()])]),
"Ignored" /* Ignored */
);
puzzleStickering.set(
m.or([m.and([LL2(), EDGES()]), edgeFR()]),
"OrientationWithoutPermutation" /* OrientationWithoutPermutation */
);
puzzleStickering.set(EDGE(["D", "F"]), "Regular" /* Regular */);
puzzleStickering.set(CENTER("F"), "Regular" /* Regular */);
break;
}
case "Void Cube": {
puzzleStickering.set(CENTERS(), "Invisible" /* Invisible */);
break;
}
case "picture":
// fallthrough
case "invisible": {
puzzleStickering.set(m.all(), "Invisible" /* Invisible */);
break;
}
case "centers-only": {
puzzleStickering.set(m.not(CENTERS()), "Ignored" /* Ignored */);
break;
}
case "opposite-centers": {
puzzleStickering.set(
m.not(m.and([CENTERS(), m.or(m.moves(["U", "D"]))])),
"Ignored" /* Ignored */
);
break;
}
case "OBL": {
puzzleStickering.set(
m.or(m.moves(["U", "D"])),
"IgnoreNonPrimary" /* IgnoreNonPrimary */
);
break;
}
default:
console.warn(
`Unsupported stickering for ${puzzleLoader.id}: ${stickering}. Setting all pieces to dim.`
);
puzzleStickering.set(m.and(m.moves([])), "Dim" /* Dim */);
}
return puzzleStickering;
}
async function cubeLikeStickeringList(puzzleID, options) {
const stickerings = [];
const stickeringsFallback = [];
for (const [name, info] of Object.entries(experimentalStickerings)) {
if (info.groups) {
if (puzzleID in info.groups) {
stickerings.push(name);
} else if (options?.use3x3x3Fallbacks && "3x3x3" in info.groups) {
stickeringsFallback.push(name);
}
}
}
return stickerings.concat(stickeringsFallback);
}
// src/cubing/puzzles/implementations/3x3x3/cube3x3x3KeyMapping.ts
var cube3x3x3KeyMapping = {
KeyI: new Move("R"),
KeyK: new Move("R'"),
KeyW: new Move("B"),
KeyO: new Move("B'"),
KeyS: new Move("D"),
KeyL: new Move("D'"),
KeyD: new Move("L"),
KeyE: new Move("L'"),
KeyJ: new Move("U"),
KeyF: new Move("U'"),
KeyH: new Move("F"),
KeyG: new Move("F'"),
KeyC: new Move("l"),
KeyR: new Move("l'"),
KeyU: new Move("r"),
KeyM: new Move("r'"),
KeyX: new Move("d"),
Comma: new Move("d'"),
KeyT: new Move("x"),
KeyY: new Move("x"),
KeyV: new Move("x'"),
KeyN: new Move("x'"),
Semicolon: new Move("y"),
KeyA: new Move("y'"),
KeyP: new Move("z"),
KeyQ: new Move("z'"),
KeyZ: new Move("M'"),
KeyB: new Move("M"),
Period: new Move("M'"),
Backquote: new Pause()
};
// src/cubing/puzzles/implementations/3x3x3/puzzle-specific-simplifications.ts
function makeSourceInfo(moveStrings, type, from, to) {
const output = [];
for (const moveString of moveStrings) {
const move = Move.fromString(moveString);
const { family, amount: direction } = move;
if (![-1, 1].includes(direction)) {
throw new Error("Invalid config move");
}
output.push({ family, direction, type, from, to });
}
return output;
}
var axisInfos = {
["x axis" /* X */]: {
sliceDiameter: 3,
extendsThroughEntirePuzzle: true,
moveSourceInfos: [
...makeSourceInfo(["R"], 0 /* INDEXABLE_SLICE_NEAR */, 0, 3),
...makeSourceInfo(["L'"], 1 /* INDEXABLE_SLICE_FAR */, 0, 3),
...makeSourceInfo(["r", "Rw"], 2 /* INDEXABLE_WIDE_NEAR */, 0, 2),
...makeSourceInfo(["l'", "Lw'"], 3 /* INDEXABLE_WIDE_FAR */, 0, 2),
...makeSourceInfo(["M'"], 4 /* SPECIFIC_SLICE */, 1, 2),
// TODO: remove some indices?
...makeSourceInfo(["x", "Uv", "Dv'"], 5 /* ROTATION */, 0, 3)
// TODO: remove some indices?
]
},
["y axis" /* Y */]: {
sliceDiameter: 3,
extendsThroughEntirePuzzle: true,
moveSourceInfos: [
...makeSourceInfo(["U"], 0 /* INDEXABLE_SLICE_NEAR */, 0, 3),
...makeSourceInfo(["D'"], 1 /* INDEXABLE_SLICE_FAR */, 0, 3),
...makeSourceInfo(["u", "Uw"], 2 /* INDEXABLE_WIDE_NEAR */, 0, 2),
...makeSourceInfo(["d'", "Dw'"], 3 /* INDEXABLE_WIDE_FAR */, 0, 2),
...makeSourceInfo(["E'"], 4 /* SPECIFIC_SLICE */, 1, 2),
// TODO: remove some indices?
...makeSourceInfo(["y", "Uv", "Dv'"], 5 /* ROTATION */, 0, 3)
// TODO: remove some indices?
]
},
["z axis" /* Z */]: {
sliceDiameter: 3,
extendsThroughEntirePuzzle: true,
moveSourceInfos: [
...makeSourceInfo(["F"], 0 /* INDEXABLE_SLICE_NEAR */, 0, 3),
...makeSourceInfo(["B'"], 1 /* INDEXABLE_SLICE_FAR */, 0, 3),
...makeSourceInfo(["f", "Fw"], 2 /* INDEXABLE_WIDE_NEAR */, 0, 3),
...makeSourceInfo(["b'", "Bw'"], 3 /* INDEXABLE_WIDE_FAR */, 0, 3),
...makeSourceInfo(["S"], 4 /* SPECIFIC_SLICE */, 1, 2),
// TODO: remove some indices?
...makeSourceInfo(["z", "Fv", "Bv'"], 5 /* ROTATION */, 0, 3)
// TODO: remove some indices?
]
}
};
var byFamily = {};
for (const [axis, info] of Object.entries(axisInfos)) {
for (const moveSourceInfo of info.moveSourceInfos) {
byFamily[moveSourceInfo.family] = { axis, moveSourceInfo };
}
}
var byAxisThenType = {};
for (const axis of Object.keys(axisInfos)) {
const entry = {};
byAxisThenType[axis] = entry;
for (const moveSourceInfo of axisInfos[axis].moveSourceInfos) {
(entry[moveSourceInfo.type] ??= []).push(moveSourceInfo);
}
}
var byAxisThenSpecificSlices = {};
for (const axis of Object.keys(axisInfos)) {
const entry = /* @__PURE__ */ new Map();
byAxisThenSpecificSlices[axis] = entry;
for (const moveSourceInfo of axisInfos[axis].moveSourceInfos) {
if (!entry.get(moveSourceInfo.from)) {
entry.set(moveSourceInfo.from, moveSourceInfo);
}
}
}
function firstOfType(axis, moveSourceType) {
const entry = byAxisThenType[axis][moveSourceType]?.[0];
if (!entry) {
throw new Error(
`Could not find a reference move (axis: ${axis}, move source type: ${moveSourceType})`
);
}
return entry;
}
var areQuantumMovesSameAxis = (quantumMove1, quantumMove2) => {
return byFamily[quantumMove1.family].axis === byFamily[quantumMove2.family].axis;
};
function simplestMove(axis, from, to, directedAmount) {
if (from + 1 === to) {
const sliceSpecificInfo = byAxisThenSpecificSlices[axis].get(from);
if (sliceSpecificInfo) {
return new Move(
new QuantumMove(sliceSpecificInfo.family),
directedAmount * sliceSpecificInfo.direction
);
}
}
const axisInfo = axisInfos[axis];
const { sliceDiameter } = axisInfo;
if (from === 0 && to === sliceDiameter) {
const moveSourceInfo2 = firstOfType(axis, 5 /* ROTATION */);
return new Move(
new QuantumMove(moveSourceInfo2.family),
directedAmount * moveSourceInfo2.direction
);
}
const far = from + to > sliceDiameter;
if (far) {
[from, to] = [sliceDiameter - to, sliceDiameter - from];
}
let outerLayer = from + 1;
let innerLayer = to;
const slice = outerLayer === innerLayer;
if (slice) {
innerLayer = null;
}
if (outerLayer === 1) {
outerLayer = null;
}
if (slice && outerLayer === 1) {
innerLayer = null;
}
if (!slice && innerLayer === 2) {
innerLayer = null;
}
const moveSourceType = slice ? far ? 1 /* INDEXABLE_SLICE_FAR */ : 0 /* INDEXABLE_SLICE_NEAR */ : far ? 3 /* INDEXABLE_WIDE_FAR */ : 2 /* INDEXABLE_WIDE_NEAR */;
const moveSourceInfo = firstOfType(axis, moveSourceType);
return new Move(
new QuantumMove(moveSourceInfo.family, innerLayer, outerLayer),
directedAmount * moveSourceInfo.direction
);
}
function simplifySameAxisMoves(moves, quantumMod = true) {
if (moves.length === 0) {
return [];
}
const axis = byFamily[moves[0].family].axis;
const axisInfo = axisInfos[axis];
const { sliceDiameter } = axisInfo;
const sliceDeltas = /* @__PURE__ */ new Map();
let lastCandidateRange = null;
function adjustValue(idx, relativeDelta) {
let newDelta = (sliceDeltas.get(idx) ?? 0) + relativeDelta;
if (quantumMod) {
newDelta = newDelta % 4 + 5 % 4 - 1;
}
if (newDelta === 0) {
sliceDeltas.delete(idx);
} else {
sliceDeltas.set(idx, newDelta);
}
}
let suffixLength = 0;
for (const move of Array.from(moves).reverse()) {
suffixLength++;
const { moveSourceInfo } = byFamily[move.family];
const directedAmount2 = move.amount * moveSourceInfo.direction;
switch (moveSourceInfo.type) {
case 0 /* INDEXABLE_SLICE_NEAR */: {
const idx = (move.innerLayer ?? 1) - 1;
adjustValue(idx, directedAmount2);
adjustValue(idx + 1, -directedAmount2);
break;
}
case 1 /* INDEXABLE_SLICE_FAR */: {
const idx = sliceDiameter - (move.innerLayer ?? 1);
adjustValue(idx, directedAmount2);
adjustValue(idx + 1, -directedAmount2);
break;
}
case 2 /* INDEXABLE_WIDE_NEAR */: {
adjustValue((move.outerLayer ?? 1) - 1, directedAmount2);
adjustValue(move.innerLayer ?? 2, -directedAmount2);
break;
}
case 3 /* INDEXABLE_WIDE_FAR */: {
adjustValue(sliceDiameter - (move.innerLayer ?? 2), directedAmount2);
adjustValue(
sliceDiameter - ((move.outerLay