UNPKG

cubing

Version:

A collection of JavaScript cubing libraries.

1,661 lines (1,643 loc) 306 kB
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