UNPKG

@vscubing/cubing

Version:

A collection of JavaScript cubing libraries.

1,636 lines (1,621 loc) 211 kB
import { DEGREES_PER_RADIAN, FreshListenerManager, HTMLElementShim, HintFaceletProp, ManagedCustomElement, NO_VALUE, RenderScheduler, SimpleTwistyPropSource, StaleDropper, Twisty3DVantage, TwistyPropDerived, TwistyPropSource, bulk3DCode, cssStyleSheetShim, customElementsShim, hintFaceletStyles, rawRenderPooled, setCameraFromOrbitCoordinates, setTwistyDebug } from "../chunks/chunk-6WKNZNVA.js"; import { countAnimatedLeaves, countLeavesInExpansionForSimultaneousMoveIndexer, countMetricMoves } from "../chunks/chunk-AJYS6ZN4.js"; import { puzzles } from "../chunks/chunk-LYOTMOJT.js"; import { PuzzleStickering, StickeringManager, cube3x3x3, customPGPuzzleLoader, getPartialAppendOptionsForPuzzleSpecificSimplifyOptions, getPieceStickeringMask } from "../chunks/chunk-SMJZNAUN.js"; import { KPattern } from "../chunks/chunk-TMCMUPQG.js"; import { Alg, AlgBuilder, Conjugate, Grouping, LineComment, Move, Newline, Pause, TraversalDownUp, TraversalUp, direct, directedGenerator, endCharIndexKey, experimentalAppendMove, functionFromTraversal, offsetMod, startCharIndexKey } from "../chunks/chunk-QVWFSWHJ.js"; // src/cubing/twisty/controllers/AnimationTypes.ts function directionScalar(direction) { return direction; } var BoundaryType = /* @__PURE__ */ ((BoundaryType2) => { BoundaryType2["Move"] = "move"; BoundaryType2["EntireTimeline"] = "entire-timeline"; return BoundaryType2; })(BoundaryType || {}); // src/cubing/twisty/controllers/indexer/AlgDuration.ts function defaultDurationForAmount(amount) { switch (Math.abs(amount)) { case 0: return 0; case 1: return 1e3; case 2: return 1500; default: return 2e3; } } var AlgDuration = class extends TraversalUp { // TODO: Pass durationForAmount as Down type instead? constructor(durationForAmount = defaultDurationForAmount) { super(); this.durationForAmount = durationForAmount; } traverseAlg(alg) { let total = 0; for (const algNode of alg.childAlgNodes()) { total += this.traverseAlgNode(algNode); } return total; } traverseGrouping(grouping) { return grouping.amount * this.traverseAlg(grouping.alg); } traverseMove(move) { return this.durationForAmount(move.amount); } 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 this.durationForAmount(1); } traverseNewline(_newline) { return this.durationForAmount(1); } traverseLineComment(_comment) { return this.durationForAmount(0); } }; // src/cubing/twisty/controllers/indexer/SimpleAlgIndexer.ts var SimpleAlgIndexer = class { constructor(kpuzzle, alg) { this.kpuzzle = kpuzzle; this.moves = new Alg(alg.experimentalExpand()); } moves; // TODO: Allow custom `durationFn`. durationFn = new AlgDuration( defaultDurationForAmount ); getAnimLeaf(index) { return Array.from(this.moves.childAlgNodes())[index]; } indexToMoveStartTimestamp(index) { const alg = new Alg(Array.from(this.moves.childAlgNodes()).slice(0, index)); return this.durationFn.traverseAlg(alg); } timestampToIndex(timestamp) { let cumulativeTime = 0; let i; for (i = 0; i < this.numAnimatedLeaves(); i++) { cumulativeTime += this.durationFn.traverseMove(this.getAnimLeaf(i)); if (cumulativeTime >= timestamp) { return i; } } return i; } patternAtIndex(index) { return this.kpuzzle.defaultPattern().applyTransformation(this.transformationAtIndex(index)); } transformationAtIndex(index) { let pattern = this.kpuzzle.identityTransformation(); for (const move of Array.from(this.moves.childAlgNodes()).slice(0, index)) { pattern = pattern.applyMove(move); } return pattern; } algDuration() { return this.durationFn.traverseAlg(this.moves); } numAnimatedLeaves() { return countAnimatedLeaves(this.moves); } moveDuration(index) { return this.durationFn.traverseMove(this.getAnimLeaf(index)); } }; // src/cubing/twisty/controllers/indexer/tree/AlgWalker.ts var AlgWalkerDecoration = class { constructor(moveCount, duration, forward, backward, children = []) { this.moveCount = moveCount; this.duration = duration; this.forward = forward; this.backward = backward; this.children = children; } }; var DecoratorConstructor = class extends TraversalUp { constructor(kpuzzle) { super(); this.kpuzzle = kpuzzle; this.identity = kpuzzle.identityTransformation(); this.dummyLeaf = new AlgWalkerDecoration( 0, 0, this.identity, this.identity, [] ); } identity; dummyLeaf; durationFn = new AlgDuration( defaultDurationForAmount ); cache = {}; traverseAlg(alg) { let moveCount = 0; let duration = 0; let transformation = this.identity; const child = []; for (const algNode of alg.childAlgNodes()) { const apd = this.traverseAlgNode(algNode); moveCount += apd.moveCount; duration += apd.duration; if (transformation === this.identity) { transformation = apd.forward; } else { transformation = transformation.applyTransformation(apd.forward); } child.push(apd); } return new AlgWalkerDecoration( moveCount, duration, transformation, transformation.invert(), child ); } traverseGrouping(grouping) { const dec = this.traverseAlg(grouping.alg); return this.mult(dec, grouping.amount, [dec]); } traverseMove(move) { const key = move.toString(); let r2 = this.cache[key]; if (r2) { return r2; } const transformation = this.kpuzzle.moveToTransformation(move); r2 = new AlgWalkerDecoration( 1, this.durationFn.traverseAlgNode(move), transformation, transformation.invert() ); this.cache[key] = r2; return r2; } traverseCommutator(commutator) { const decA = this.traverseAlg(commutator.A); const decB = this.traverseAlg(commutator.B); const AB = decA.forward.applyTransformation(decB.forward); const ApBp = decA.backward.applyTransformation(decB.backward); const ABApBp = AB.applyTransformation(ApBp); const dec = new AlgWalkerDecoration( 2 * (decA.moveCount + decB.moveCount), 2 * (decA.duration + decB.duration), ABApBp, ABApBp.invert(), [decA, decB] ); return this.mult(dec, 1, [dec, decA, decB]); } traverseConjugate(conjugate) { const decA = this.traverseAlg(conjugate.A); const decB = this.traverseAlg(conjugate.B); const AB = decA.forward.applyTransformation(decB.forward); const ABAp = AB.applyTransformation(decA.backward); const dec = new AlgWalkerDecoration( 2 * decA.moveCount + decB.moveCount, 2 * decA.duration + decB.duration, ABAp, ABAp.invert(), [decA, decB] ); return this.mult(dec, 1, [dec, decA, decB]); } traversePause(pause) { if (pause.experimentalNISSGrouping) { return this.dummyLeaf; } return new AlgWalkerDecoration( 1, this.durationFn.traverseAlgNode(pause), this.identity, this.identity ); } traverseNewline(_newline) { return this.dummyLeaf; } traverseLineComment(_comment) { return this.dummyLeaf; } mult(apd, n, child) { const absn = Math.abs(n); const st = apd.forward.selfMultiply(n); return new AlgWalkerDecoration( apd.moveCount * absn, apd.duration * absn, st, st.invert(), child ); } }; var WalkerDown = class { constructor(apd, back) { this.apd = apd; this.back = back; } }; var AlgWalker = class extends TraversalDownUp { constructor(kpuzzle, algOrAlgNode, apd) { super(); this.kpuzzle = kpuzzle; this.algOrAlgNode = algOrAlgNode; this.apd = apd; this.i = -1; this.dur = -1; this.goalIndex = -1; this.goalDuration = -1; this.move = void 0; this.back = false; this.moveDuration = 0; this.st = this.kpuzzle.identityTransformation(); this.root = new WalkerDown(this.apd, false); } move; moveDuration; back; st; root; i; dur; goalIndex; goalDuration; moveByIndex(loc) { if (this.i >= 0 && this.i === loc) { return this.move !== void 0; } return this.dosearch(loc, Infinity); } moveByDuration(dur) { if (this.dur >= 0 && this.dur < dur && this.dur + this.moveDuration >= dur) { return this.move !== void 0; } return this.dosearch(Infinity, dur); } dosearch(loc, dur) { this.goalIndex = loc; this.goalDuration = dur; this.i = 0; this.dur = 0; this.move = void 0; this.moveDuration = 0; this.back = false; this.st = this.kpuzzle.identityTransformation(); const r2 = this.algOrAlgNode.is(Alg) ? this.traverseAlg(this.algOrAlgNode, this.root) : this.traverseAlgNode(this.algOrAlgNode, this.root); return r2; } traverseAlg(alg, wd) { if (!this.firstcheck(wd)) { return false; } let i = wd.back ? alg.experimentalNumChildAlgNodes() - 1 : 0; for (const algNode of directedGenerator( alg.childAlgNodes(), wd.back ? -1 /* Backwards */ : 1 /* Forwards */ )) { if (this.traverseAlgNode( algNode, new WalkerDown(wd.apd.children[i], wd.back) )) { return true; } i += wd.back ? -1 : 1; } return false; } traverseGrouping(grouping, wd) { if (!this.firstcheck(wd)) { return false; } const back = this.domult(wd, grouping.amount); return this.traverseAlg( grouping.alg, new WalkerDown(wd.apd.children[0], back) ); } traverseMove(move, wd) { if (!this.firstcheck(wd)) { return false; } this.move = move; this.moveDuration = wd.apd.duration; this.back = wd.back; return true; } traverseCommutator(commutator, wd) { if (!this.firstcheck(wd)) { return false; } const back = this.domult(wd, 1); if (back) { return this.traverseAlg( commutator.B, new WalkerDown(wd.apd.children[2], !back) ) || this.traverseAlg( commutator.A, new WalkerDown(wd.apd.children[1], !back) ) || this.traverseAlg( commutator.B, new WalkerDown(wd.apd.children[2], back) ) || this.traverseAlg(commutator.A, new WalkerDown(wd.apd.children[1], back)); } else { return this.traverseAlg( commutator.A, new WalkerDown(wd.apd.children[1], back) ) || this.traverseAlg( commutator.B, new WalkerDown(wd.apd.children[2], back) ) || this.traverseAlg( commutator.A, new WalkerDown(wd.apd.children[1], !back) ) || this.traverseAlg( commutator.B, new WalkerDown(wd.apd.children[2], !back) ); } } traverseConjugate(conjugate, wd) { if (!this.firstcheck(wd)) { return false; } const back = this.domult(wd, 1); if (back) { return this.traverseAlg( conjugate.A, new WalkerDown(wd.apd.children[1], !back) ) || this.traverseAlg( conjugate.B, new WalkerDown(wd.apd.children[2], back) ) || this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], back)); } else { return this.traverseAlg( conjugate.A, new WalkerDown(wd.apd.children[1], back) ) || this.traverseAlg( conjugate.B, new WalkerDown(wd.apd.children[2], back) ) || this.traverseAlg(conjugate.A, new WalkerDown(wd.apd.children[1], !back)); } } traversePause(pause, wd) { if (!this.firstcheck(wd)) { return false; } this.move = pause; this.moveDuration = wd.apd.duration; this.back = wd.back; return true; } traverseNewline(_newline, _wd) { return false; } traverseLineComment(_lineComment, _wd) { return false; } firstcheck(wd) { if (wd.apd.moveCount + this.i <= this.goalIndex && wd.apd.duration + this.dur < this.goalDuration) { return this.keepgoing(wd); } return true; } domult(wd, amount) { let back = wd.back; if (amount === 0) { return back; } if (amount < 0) { back = !back; amount = -amount; } const base = wd.apd.children[0]; const full = Math.min( Math.floor((this.goalIndex - this.i) / base.moveCount), Math.ceil((this.goalDuration - this.dur) / base.duration - 1) ); if (full > 0) { this.keepgoing(new WalkerDown(base, back), full); } return back; } keepgoing(wd, mul = 1) { this.i += mul * wd.apd.moveCount; this.dur += mul * wd.apd.duration; if (mul !== 1) { if (wd.back) { this.st = this.st.applyTransformation( wd.apd.backward.selfMultiply(mul) ); } else { this.st = this.st.applyTransformation(wd.apd.forward.selfMultiply(mul)); } } else { if (wd.back) { this.st = this.st.applyTransformation(wd.apd.backward); } else { this.st = this.st.applyTransformation(wd.apd.forward); } } return false; } }; // src/cubing/twisty/controllers/indexer/tree/chunkAlgs.ts var MIN_CHUNKING_THRESHOLD = 16; function chunkifyAlg(alg, chunkMaxLength) { const mainAlgBuilder = new AlgBuilder(); const chunkAlgBuilder = new AlgBuilder(); for (const algNode of alg.childAlgNodes()) { chunkAlgBuilder.push(algNode); if (chunkAlgBuilder.experimentalNumAlgNodes() >= chunkMaxLength) { mainAlgBuilder.push(new Grouping(chunkAlgBuilder.toAlg())); chunkAlgBuilder.reset(); } } mainAlgBuilder.push(new Grouping(chunkAlgBuilder.toAlg())); return mainAlgBuilder.toAlg(); } var ChunkAlgs = class extends TraversalUp { traverseAlg(alg) { const algLength = alg.experimentalNumChildAlgNodes(); if (algLength < MIN_CHUNKING_THRESHOLD) { return alg; } return chunkifyAlg(alg, Math.ceil(Math.sqrt(algLength))); } traverseGrouping(grouping) { return new Grouping( this.traverseAlg(grouping.alg), grouping.amount // TODO ); } traverseMove(move) { return move; } traverseCommutator(commutator) { return new Conjugate( this.traverseAlg(commutator.A), this.traverseAlg(commutator.B) ); } traverseConjugate(conjugate) { return new Conjugate( this.traverseAlg(conjugate.A), this.traverseAlg(conjugate.B) ); } traversePause(pause) { return pause; } traverseNewline(newline) { return newline; } traverseLineComment(comment) { return comment; } }; var chunkAlgs = functionFromTraversal(ChunkAlgs); // src/cubing/twisty/controllers/indexer/tree/TreeAlgIndexer.ts var TreeAlgIndexer = class { constructor(kpuzzle, alg) { this.kpuzzle = kpuzzle; const deccon = new DecoratorConstructor(this.kpuzzle); const chunkedAlg = chunkAlgs(alg); this.decoration = deccon.traverseAlg(chunkedAlg); this.walker = new AlgWalker(this.kpuzzle, chunkedAlg, this.decoration); } decoration; walker; getAnimLeaf(index) { if (this.walker.moveByIndex(index)) { if (!this.walker.move) { throw new Error("`this.walker.mv` missing"); } const move = this.walker.move; if (this.walker.back) { return move.invert(); } return move; } return null; } indexToMoveStartTimestamp(index) { if (this.walker.moveByIndex(index) || this.walker.i === index) { return this.walker.dur; } throw new Error(`Out of algorithm: index ${index}`); } indexToMovesInProgress(index) { if (this.walker.moveByIndex(index) || this.walker.i === index) { return this.walker.dur; } throw new Error(`Out of algorithm: index ${index}`); } patternAtIndex(index, startPattern) { this.walker.moveByIndex(index); return (startPattern ?? this.kpuzzle.defaultPattern()).applyTransformation( this.walker.st ); } // TransformAtIndex does not reflect the start pattern; it only reflects // the change from the start pattern to the current move index. If you // want the actual pattern, use patternAtIndex. transformationAtIndex(index) { this.walker.moveByIndex(index); return this.walker.st; } numAnimatedLeaves() { return this.decoration.moveCount; } timestampToIndex(timestamp) { this.walker.moveByDuration(timestamp); return this.walker.i; } algDuration() { return this.decoration.duration; } moveDuration(index) { this.walker.moveByIndex(index); return this.walker.moveDuration; } }; // src/cubing/twisty/model/props/viewer/BackViewProp.ts var backViewLayouts = { none: true, // default "side-by-side": true, "top-right": true }; var BackViewProp = class extends SimpleTwistyPropSource { getDefaultValue() { return "auto"; } }; // src/cubing/twisty/patternChecker.ts var getSolveAnalyzer = async (puzzleLoader) => { const kpuzzle = await puzzleLoader.kpuzzle(); const IGNORED_PIECE_VALUE = 9999; const ORIENTATION_ONLY_PIECE_VALUE = 9998; function applyPuzzleStickering(pattern, flatPuzzleStickering) { const newPatternData = {}; for (const orbitDefinition of kpuzzle.definition.orbits) { const patternOrbit = pattern.patternData[orbitDefinition.orbitName]; const maskOrbit = flatPuzzleStickering[orbitDefinition.orbitName]; const newOrbitData = { pieces: [], orientation: [], orientationMod: [] }; for (let i = 0; i < orbitDefinition.numPieces; i++) { switch (maskOrbit[i]) { case "PermuteNonPrimary" /* PermuteNonPrimary */: // fallthrough case "Regular" /* Regular */: // fallthrough case "Dim" /* Dim */: { newOrbitData.pieces.push(patternOrbit.pieces[i]); newOrbitData.orientation.push(patternOrbit.orientation[i]); newOrbitData.orientationMod.push( patternOrbit.orientationMod?.[i] ?? 0 ); break; } case "Ignored" /* Ignored */: // fallthrough case "Invisible" /* Invisible */: { newOrbitData.pieces.push(IGNORED_PIECE_VALUE); newOrbitData.orientation.push(0); newOrbitData.orientationMod.push(1); break; } case "IgnoreNonPrimary" /* IgnoreNonPrimary */: // fallthrough case "Ignoriented" /* Ignoriented */: // fallthrough case "OrientationWithoutPermutation" /* OrientationWithoutPermutation */: // fallthrough case "OrientationStickers" /* OrientationStickers */: { newOrbitData.pieces.push(ORIENTATION_ONLY_PIECE_VALUE); newOrbitData.orientation.push(patternOrbit.orientation[i]); newOrbitData.orientationMod.push( patternOrbit.orientationMod?.[i] ?? 0 ); break; } // case PieceStickering.Mystery: { // newOrbitData.pieces.push(MYSTERY_PIECE_VALUE); // newOrbitData.orientation.push(0); // newOrbitData.orientationMod.push(1); // break; // } default: { throw new Error( `Unrecognized \`PieceMaskAction\` value: ${maskOrbit[i]}` ); } } } newPatternData[orbitDefinition.orbitName] = newOrbitData; } return new KPattern(pattern.kpuzzle, newPatternData); } const cubeOrientations = []; for (const moveToSetU of [ null, new Move("x"), new Move("x2"), new Move("x'"), new Move("z"), new Move("z'") ]) { for (const moveToSetF of [ null, new Move("y"), new Move("y2"), new Move("y'") ]) { const algBuilder = new AlgBuilder(); if (moveToSetU) { algBuilder.push(moveToSetU); } if (moveToSetF) { algBuilder.push(moveToSetF); } const algToNormalize = algBuilder.toAlg(); const inverseTransformation = kpuzzle.algToTransformation(algToNormalize); cubeOrientations.push({ inverseTransformation, algToNormalize }); } } const orientedSolvedPattern = kpuzzle.defaultPattern(); class PatternChecker { constructor(name, flatPuzzleStickering, orientationAnchor, obviates = []) { this.name = name; this.flatPuzzleStickering = flatPuzzleStickering; this.orientationAnchor = orientationAnchor; this.obviates = obviates; for (const cubeOrientation of cubeOrientations) { const orientedPattern = orientedSolvedPattern.applyTransformation( cubeOrientation.inverseTransformation ); const maskedPattern = applyPuzzleStickering( orientedPattern, flatPuzzleStickering ); const { anchorPieceIndex, anchorOrientationIndex } = this.extractAnchorCoordinates(orientedPattern); const byOrientation = this.solvedPatternsByAnchorCoordinates[anchorPieceIndex] ??= {}; byOrientation[anchorOrientationIndex] = maskedPattern; } } solvedPatternsByAnchorCoordinates = {}; extractAnchorCoordinates(pattern) { const orbitData = pattern.patternData[this.orientationAnchor.orbitName]; if ((orbitData.orientationMod?.[this.orientationAnchor.pieceIndex] ?? 0) !== 0) { throw new Error("Unexpected partially known orientation"); } return { anchorPieceIndex: orbitData.pieces[this.orientationAnchor.pieceIndex], anchorOrientationIndex: orbitData.orientation[this.orientationAnchor.pieceIndex] }; } check(candidateFull3x3x3Pattern) { for (const cubeOrientation of cubeOrientations) { const reorientedCandidate = candidateFull3x3x3Pattern.applyTransformation( cubeOrientation.inverseTransformation ); const candidateMasked = applyPuzzleStickering( reorientedCandidate, this.flatPuzzleStickering ); const { anchorPieceIndex, anchorOrientationIndex } = this.extractAnchorCoordinates(reorientedCandidate); const solvedPatternByDRF = this.solvedPatternsByAnchorCoordinates[anchorPieceIndex][anchorOrientationIndex]; if (candidateMasked.isIdentical(solvedPatternByDRF)) { const { algToNormalize } = cubeOrientation; return { isSolved: true, algToNormalize }; } } return { isSolved: false }; } } const R = "Regular" /* Regular */; const I = "Ignored" /* Ignored */; const F2L1 = [ { EDGES: [I, I, I, I, R, R, R, R, R, I, I, I], CORNERS: [I, I, I, I, R, I, I, I], CENTERS: [I, R, R, R, R, R] }, { orbitName: "EDGES", pieceIndex: 4 } ]; const F2L2A = [ { EDGES: [I, I, I, I, R, R, R, R, R, R, I, I], CORNERS: [I, I, I, I, R, R, I, I], CENTERS: [I, R, R, R, R, R] }, { orbitName: "EDGES", pieceIndex: 4 } ]; const F2L2O = [ { EDGES: [I, I, I, I, R, R, R, R, R, I, I, R], CORNERS: [I, I, I, I, R, I, R, I], CENTERS: [I, R, R, R, R, R] }, { orbitName: "EDGES", pieceIndex: 4 } ]; const F2L3 = [ { EDGES: [I, I, I, I, R, R, R, R, I, R, R, R], CORNERS: [I, I, I, I, I, R, R, R], CENTERS: [I, R, R, R, R, R] }, { orbitName: "EDGES", pieceIndex: 4 } ]; const FirstLayer = [ { EDGES: [I, I, I, I, R, R, R, R, I, I, I, I], CORNERS: [I, I, I, I, R, R, R, R], CENTERS: [I, R, R, R, R, R] }, { orbitName: "EDGES", pieceIndex: 4 } ]; const Roux1L = [ { EDGES: [I, I, I, I, I, I, I, R, I, R, I, R], CORNERS: [I, I, I, I, I, R, R, I], CENTERS: [I, R, I, I, I, I] }, { orbitName: "CORNERS", pieceIndex: 5 } ]; const Roux2 = [ { EDGES: [I, I, I, I, I, R, I, R, R, R, R, R], CORNERS: [I, I, I, I, R, R, R, R], CENTERS: [I, R, I, R, I, I] }, { orbitName: "CORNERS", pieceIndex: 5 } ]; const patternCheckers = []; async function addSimpleStep(stickering, orbitName, pieceIndex, name, obviates = []) { patternCheckers.push( new PatternChecker( name ?? stickering, Object.fromEntries( (await cubeLikePuzzleStickering(puzzleLoader, stickering)).stickerings.entries() ), { orbitName, pieceIndex }, obviates ) ); } const LSLLStuff = ["OLL", "OCLL", "EOLL", "F2L", "CLS", "ELS"]; const XCrosses = [ "X-Cross", "Double X-Cross (adjacent)", "Double X-Cross (opposite)", "Triple X-Cross", "First Layer" ]; const RouxBlocks = ["Both Roux blocks", "1st Roux block"]; const CFOP_Stuff = [...XCrosses, ...LSLLStuff]; const XRoux = [...XCrosses, ...RouxBlocks]; await addSimpleStep("full", "EDGES", 4, "Solved"); await addSimpleStep("PLL", "EDGES", 4); await addSimpleStep("L6E", "EDGES", 4); await addSimpleStep("OLL", "EDGES", 4, void 0, [ "ELS", "CLS", "CLL", "Solved", "L6E" ]); await addSimpleStep("OLL", "EDGES", 4); await addSimpleStep("OCLL", "EDGES", 4); await addSimpleStep("EOLL", "EDGES", 4); await addSimpleStep("F2L", "EDGES", 4, void 0, ["CLS"]); await addSimpleStep("CLS", "EDGES", 4, void 0, [ "OLL", "OCLL", "EOLL", "F2L", "Solved", "L6E" ]); await addSimpleStep("ELS", "EDGES", 4, void 0, [ "OLL", "OCLL", "EOLL", "F2L" ]); patternCheckers.push(new PatternChecker("Triple X-Cross", ...F2L3, XRoux)); patternCheckers.push(new PatternChecker("F2L Slot 3", ...F2L3, XRoux)); patternCheckers.push( new PatternChecker("Double X-Cross (opposite)", ...F2L2O, XRoux) ); patternCheckers.push( new PatternChecker("Double X-Cross (adjacent)", ...F2L2A, XRoux) ); patternCheckers.push( new PatternChecker("F2L Slot 2 (adjacent)", ...F2L2A, XRoux) ); patternCheckers.push( new PatternChecker("F2L Slot 2 (opposite)", ...F2L2O, XRoux) ); patternCheckers.push(new PatternChecker("X-Cross", ...F2L1, XRoux)); patternCheckers.push(new PatternChecker("First Layer", ...FirstLayer, XRoux)); patternCheckers.push(new PatternChecker("F2L Slot 1", ...F2L1, XRoux)); await addSimpleStep("2x2x3", "CORNERS", 6); await addSimpleStep("CMLL", "CORNERS", 4, void 0, CFOP_Stuff); patternCheckers.push( new PatternChecker("Both Roux blocks", ...Roux2, [ ...CFOP_Stuff, "Solved", "PLL" ]) ); patternCheckers.push( new PatternChecker("1st Roux block", ...Roux1L, CFOP_Stuff) ); await addSimpleStep("Cross", "EDGES", 4, void 0, XCrosses); await addSimpleStep("2x2x2", "CORNERS", 6); const obviated = /* @__PURE__ */ new Set(); let lastI = patternCheckers.length + 1; return function multiCheck(pattern) { for (const [i, patternChecker] of patternCheckers.entries()) { if (i >= lastI) { return null; } if (obviated.has(patternChecker.name)) { continue; } const isSolvedInfo = patternChecker.check(pattern); if (isSolvedInfo.isSolved) { lastI = i; obviated.add(patternChecker.name); for (const newlyObviated of patternChecker.obviates) { obviated.add(newlyObviated); } return patternChecker.name; } else { } } return null; }; async function cubeLikePuzzleStickering(puzzleLoader2, stickering) { const kpuzzle2 = await puzzleLoader2.kpuzzle(); const puzzleStickering = new PuzzleStickering(kpuzzle2); const m = new StickeringManager(kpuzzle2); const LL = () => 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(LL()); 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([LL(), EDGES()])]); const centerLL = () => m.and([LL(), 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(LL())]); const slotFR = () => m.or([cornerDFR(), edgeFR()]); function dimF2L() { puzzleStickering.set(F2L(), "Dim" /* Dim */); } function setPLL() { puzzleStickering.set(LL(), "PermuteNonPrimary" /* PermuteNonPrimary */); puzzleStickering.set(centerLL(), "Dim" /* Dim */); } function setOLL() { puzzleStickering.set(LL(), "IgnoreNonPrimary" /* IgnoreNonPrimary */); puzzleStickering.set(centerLL(), "Regular" /* Regular */); } function dimOLL() { puzzleStickering.set(LL(), "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(LL(), "Ignoriented" /* Ignoriented */); puzzleStickering.set(m.and([LL(), CENTERS()]), "Dim" /* Dim */); puzzleStickering.set( m.and([LL(), CORNERS()]), "IgnoreNonPrimary" /* IgnoreNonPrimary */ ); break; } case "OLL": { dimF2L(); setOLL(); break; } case "EOLL": { dimF2L(); setOLL(); puzzleStickering.set(m.and([LL(), CORNERS()]), "Ignored" /* Ignored */); break; } case "COLL": { dimF2L(); puzzleStickering.set( m.and([LL(), EDGES()]), "Ignoriented" /* Ignoriented */ ); puzzleStickering.set(m.and([LL(), CENTERS()]), "Dim" /* Dim */); puzzleStickering.set(m.and([LL(), CORNERS()]), "Regular" /* Regular */); break; } case "OCLL": { dimF2L(); dimOLL(); puzzleStickering.set( m.and([LL(), CORNERS()]), "IgnoreNonPrimary" /* IgnoreNonPrimary */ ); break; } case "CPLL": { dimF2L(); puzzleStickering.set( m.and([CORNERS(), LL()]), "PermuteNonPrimary" /* PermuteNonPrimary */ ); puzzleStickering.set( m.and([m.not(CORNERS()), LL()]), "Dim" /* Dim */ ); break; } case "CLL": { dimF2L(); puzzleStickering.set( m.not(m.and([CORNERS(), LL()])), "Dim" /* Dim */ ); break; } case "EPLL": { dimF2L(); puzzleStickering.set(LL(), "Dim" /* Dim */); puzzleStickering.set( m.and([LL(), EDGES()]), "PermuteNonPrimary" /* PermuteNonPrimary */ ); break; } case "ELL": { dimF2L(); puzzleStickering.set(LL(), "Dim" /* Dim */); puzzleStickering.set(m.and([LL(), EDGES()]), "Regular" /* Regular */); break; } case "ELS": { dimF2L(); setOLL(); puzzleStickering.set(m.and([LL(), CORNERS()]), "Ignored" /* Ignored */); puzzleStickering.set(edgeFR(), "Regular" /* Regular */); puzzleStickering.set(cornerDFR(), "Ignored" /* Ignored */); break; } case "LL": { dimF2L(); break; } case "F2L": { puzzleStickering.set(LL(), "Ignored" /* Ignored */); break; } case "ZBLL": { dimF2L(); puzzleStickering.set(LL(), "PermuteNonPrimary" /* PermuteNonPrimary */); puzzleStickering.set(centerLL(), "Dim" /* Dim */); puzzleStickering.set(m.and([LL(), CORNERS()]), "Regular" /* Regular */); break; } case "ZBLS": { dimF2L(); puzzleStickering.set(slotFR(), "Regular" /* Regular */); setOLL(); puzzleStickering.set(m.and([LL(), 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([LL(), EDGES()]), "Ignoriented" /* Ignoriented */ ); puzzleStickering.set(m.and([LL(), CENTERS()]), "Dim" /* Dim */); puzzleStickering.set( m.and([LL(), CORNERS()]), "IgnoreNonPrimary" /* IgnoreNonPrimary */ ); break; } case "LS": { dimF2L(); puzzleStickering.set(slotFR(), "Regular" /* Regular */); puzzleStickering.set(LL(), "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([LL(), 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([LL(), CORNERS()]), "Regular" /* Regular */); break; } case "L10P": { puzzleStickering.set(m.not(L6E()), "Dim" /* Dim */); puzzleStickering.set(m.and([CORNERS(), LL()]), "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(LL())])), "Ignored" /* Ignored */ ); puzzleStickering.set(CENTER("R"), "Dim" /* Dim */); break; } case "SecondBlock": { puzzleStickering.set( m.not(m.and([m.and(m.moves(["L"])), m.not(LL())])), "Ignored" /* Ignored */ ); puzzleStickering.set( m.and([m.and(m.moves(["L"])), m.not(LL())]), "Dim" /* Dim */ ); puzzleStickering.set( m.and([m.and(m.moves(["R"])), m.not(LL())]), "Regular" /* Regular */ ); break; } case "EODF": { dimF2L(); puzzleStickering.set( m.or([cornerDFR(), m.and([LL(), CORNERS()])]), "Ignored" /* Ignored */ ); puzzleStickering.set( m.or([m.and([LL(), 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; } default: console.warn( `Unsupported stickering for ${puzzleLoader2.id}: ${stickering}. Setting all pieces to dim.` ); puzzleStickering.set(m.and(m.moves([])), "Dim" /* Dim */); } return puzzleStickering; } }; // src/cubing/twisty/views/2D/TwistyAnimatedSVG.ts var xmlns = "http://www.w3.org/2000/svg"; var DATA_COPY_ID_ATTRIBUTE = "data-copy-id"; var svgCounter = 0; function nextSVGID() { svgCounter += 1; return `svg${svgCounter.toString()}`; } var colorMaps = { dim: { white: "#dddddd", orange: "#884400", limegreen: "#008800", red: "#660000", "rgb(34, 102, 255)": "#000088", // TODO yellow: "#888800", "rgb(102, 0, 153)": "rgb(50, 0, 76)", purple: "#3f003f" }, oriented: "#44ddcc", ignored: "#555555", invisible: "#00000000" }; var TwistyAnimatedSVG = class { constructor(kpuzzle, svgSource, experimentalStickeringMask, showUnknownOrientations = false) { this.kpuzzle = kpuzzle; this.showUnknownOrientations = showUnknownOrientations; if (!svgSource) { throw new Error(`No SVG definition for puzzle type: ${kpuzzle.name()}`); } this.svgID = nextSVGID(); this.wrapperElement = document.createElement("div"); this.wrapperElement.classList.add("svg-wrapper"); this.wrapperElement.innerHTML = svgSource; const svgElem = this.wrapperElement.querySelector("svg"); if (!svgElem) { throw new Error("Could not get SVG element"); } this.svgElement = svgElem; if (xmlns !== svgElem.namespaceURI) { throw new Error("Unexpected XML namespace"); } svgElem.style.maxWidth = "100%"; svgElem.style.maxHeight = "100%"; this.gradientDefs = document.createElementNS(xmlns, "defs"); svgElem.insertBefore(this.gradientDefs, svgElem.firstChild); for (const orbitDefinition of kpuzzle.definition.orbits) { for (let idx = 0; idx < orbitDefinition.numPieces; idx++) { for (let orientation = 0; orientation < orbitDefinition.numOrientations; orientation++) { const id = this.elementID( orbitDefinition.orbitName, idx, orientation ); const elem = this.elementByID(id); let originalColor = elem?.style.fill; if (experimentalStickeringMask) { (() => { const a = experimentalStickeringMask.orbits; if (!a) { return; } const orbitStickeringMask = a[orbitDefinition.orbitName]; if (!orbitStickeringMask) { return; } const pieceStickeringMask = orbitStickeringMask.pieces[idx]; if (!pieceStickeringMask) { return; } const faceletStickeringMasks = pieceStickeringMask.facelets[orientation]; if (!faceletStickeringMasks) { return; } const stickeringMask = typeof faceletStickeringMasks === "string" ? faceletStickeringMasks : faceletStickeringMasks?.mask; const colorMap = colorMaps[stickeringMask]; if (typeof colorMap === "string") { originalColor = colorMap; } else if (colorMap) { originalColor = colorMap[originalColor]; } })(); } else { originalColor = elem?.style.fill; } this.originalColors[id] = originalColor; this.gradients[id] = this.newGradient(id, originalColor); this.gradientDefs.appendChild(this.gradients[id]); elem?.setAttribute("style", `fill: url(#grad-${this.svgID}-${id})`); } } } for (const hintElem of Array.from( svgElem.querySelectorAll(`[${DATA_COPY_ID_ATTRIBUTE}]`) )) { const id = hintElem.getAttribute(DATA_COPY_ID_ATTRIBUTE); hintElem.setAttribute("style", `fill: url(#grad-${this.svgID}-${id})`); } if (this.showUnknownOrientations) { this.drawPattern(this.kpuzzle.defaultPattern()); } } wrapperElement; svgElement; gradientDefs; originalColors = {}; gradients = {}; svgID; drawPattern(pattern, nextPattern, fraction) { this.draw(pattern, nextPattern, fraction); } // TODO: save definition in the constructor? draw(pattern, nextPattern, fraction) { const nextTransformation = nextPattern?.experimentalToTransformation(); if (!pattern) { throw new Error("Distinguishable pieces are not handled for SVG yet!"); } for (const orbitDefinition of pattern.kpuzzle.definition.orbits) { const currentPatternOrbit = pattern.patternData[orbitDefinition.orbitName]; const nextTransformationOrbit = nextTransformation ? nextTransformation.transformationData[orbitDefinition.orbitName] : null; for (let idx = 0; idx < orbitDefinition.numPieces; idx++) { for (let orientation = 0; orientation < orbitDefinition.numOrientations; orientation++) { const id = this.elementID( orbitDefinition.orbitName, idx, orientation ); const fromCur = this.elementID( orbitDefinition.orbitName, currentPatternOrbit.pieces[idx], (orbitDefinition.numOrientations - currentPatternOrbit.orientation[idx] + orientation) % orbitDefinition.numOrientations ); let singleColor = false; if (nextTransformationOrbit) { const fromNext = this.elementID( orbitDefinition.orbitName, nextTransformationOrbit.permutation[idx], (orbitDefinition.numOrientations - nextTransformationOrbit.orientationDelta[idx] + orientation) % orbitDefinition.numOrientations ); if (fromCur === fromNext) { singleColor = true; } fraction = fraction || 0; const easedBackwardsPercent = 100 * (1 - fraction * fraction * (2 - fraction * fraction)); this.gradients[id].children[0].setAttribute( "stop-color", this.originalColors[fromCur] ); this.gradients[id].children[0].setAttribute( "offset", `${Math.max(easedBackwardsPercent - 5, 0)}%` ); this.gradients[id].children[1].setAttribute( "offset", `${Math.max(easedBackwardsPercent - 5, 0)}%` ); this.gradients[id].children[2].setAttribute( "offset", `${easedBackwardsPercent}%` ); this.gradients[id].children[3].setAttribute( "offset", `${easedBackwardsPercent}%` ); this.gradients[id].children[3].setAttribute( "stop-color", this.originalColors[fromNext] ); } else { singleColor = true; } if (singleColor) { if (this.showUnknownOrientations && currentPatternOrbit.orientationMod?.[idx] === 1) { this.gradients[id].children[0].setAttribute("stop-color", "#000"); this.gradients[id].children[0].setAttribute("offset", "5%"); this.gradients[id].children[1].setAttribute("offset", "5%"); this.gradients[id].children[2].setAttribute("offset", "20%"); this.gradients[id].children[3].setAttribute("offset", "20%"); this.gradients[id].children[3].setAttribute( "stop-color", this.originalColors[fromCur] ); } else { this.gradients[id].children[0].setAttribute( "stop-color", this.originalColors[fromCur] ); this.gradients[id].children[0].setAttribute("offset", "100%"); this.gradients[id].children[1].setAttribute("offset", "100%"); this.gradients[id].children[2].setAttribute("offset", "100%"); this.gradients[id].children[3].setAttribute("offset", "100%"); } } } } } } newGradient(id, originalColor) { const grad = document.createElementNS( xmlns, "radialGradient" ); grad.setAttribute("id", `grad-${this.svgID}-${id}`); grad.setAttribute("r", "70.7107%"); const stopDefs = [ { offset: 0, color: originalColor }, { offset: 0, color: "black" }, { offset: 0, color: "black" }, { offset: 0, color: originalColor } ]; for (const stopDef of stopDefs) { const stop = document.createElementNS(xmlns, "stop"); stop.setAttribute("offset", `${stopDef.offset}%`); stop.setAttribute("stop-color", stopDef.color); stop.setAttribute("stop-opacity", "1"); grad.appendChild(stop); } return grad; } elementID(orbitName, idx, orientation) { return `${orbitName}-l${idx}-o${orientation}`; } elementByID(id) { return this.wrapperElement.querySelector(`#${id}`); } }; // src/cubing/twisty/views/document.ts var globalSafeDocument = typeof document === "undefined" ? null : document; // src/cubing/twisty/views/control-panel/TwistyScrubber.css.ts var twistyScrubberCSS = new cssStyleSheetShim(); twistyScrubberCSS.replaceSync( ` :host { width: 384px; height: 16px; display: grid; } .wrapper { width: 100%; height: 100%; display: grid; overflow: hidden; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); background: rgba(196, 196, 196, 0.75); } input:not(:disabled) { cursor: ew-resize; } .wrapper.dark-mode { background: #666666; } ` ); // src/cubing/twisty/views/control-panel/TwistyScrubber.ts var SLOW_DOWN_SCRUBBING = false; var isMouseDown = false; globalSafeDocument?.addEventListener( "mousedown", (event) => { if (event.which) { isMouseDown