@vscubing/cubing
Version:
A collection of JavaScript cubing libraries.
1,347 lines (1,286 loc) • 85.9 kB
TypeScript
import { Texture, Object3D, Raycaster, PerspectiveCamera, Scene, WebGLRenderer } from 'three/src/Three.js';
type ExperimentalNotationType = "auto" | "LGN";
interface ExperimentalSerializationOptions {
notation?: ExperimentalNotationType;
}
declare enum IterationDirection {
Forwards = 1,
Backwards = -1
}
declare abstract class Comparable {
is(c: any): boolean;
as<T>(c: new (...args: any) => T): T | null;
abstract isIdentical(other: Comparable): boolean;
}
interface Repeatable extends Comparable {
experimentalExpand(iterDir?: IterationDirection, depth?: number): Generator<AlgLeaf>;
}
declare abstract class AlgCommon<T extends Alg | AlgNode> extends Comparable implements Repeatable {
constructor();
get log(): (message?: any) => void;
abstract toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
abstract invert(): T;
abstract experimentalExpand(iterDir: IterationDirection): Generator<AlgLeaf>;
}
interface QuantumMoveModifications {
outerLayer?: number;
innerLayer?: number;
family?: string;
}
declare class QuantumMove extends Comparable {
#private;
constructor(family: string, innerLayer?: number | null, outerLayer?: number | null);
static fromString(s: string): QuantumMove;
modified(modifications: QuantumMoveModifications): QuantumMove;
isIdentical(other: QuantumMove): boolean;
/** @deprecated */
get family(): string;
/** @deprecated */
get outerLayer(): number | null;
/** @deprecated */
get innerLayer(): number | null;
experimentalExpand(): Generator<AlgLeaf>;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
}
interface MoveModifications {
outerLayer?: number;
innerLayer?: number;
family?: string;
amount?: number;
}
/** @category Alg Nodes */
declare class Move extends AlgCommon<Move> {
#private;
constructor(...args: [QuantumMove] | [QuantumMove, number] | [string] | [string, number]);
isIdentical(other: Comparable): boolean;
invert(): Move;
experimentalExpand(iterDir?: IterationDirection): Generator<AlgLeaf>;
get quantum(): QuantumMove;
modified(modifications: MoveModifications): Move;
static fromString(s: string): Move;
get amount(): number;
/** @deprecated */
get type(): string;
/** @deprecated */
get family(): string;
/** @deprecated */
get outerLayer(): number | undefined;
/** @deprecated */
get innerLayer(): number | undefined;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
}
/** @category Alg Nodes */
declare class Pause extends AlgCommon<Pause> {
experimentalNISSGrouping?: Grouping;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
isIdentical(other: Comparable): boolean;
invert(): Pause;
experimentalExpand(_iterDir?: IterationDirection, _depth?: number): Generator<AlgLeaf>;
}
/** @category Alg Nodes */
declare class Grouping extends AlgCommon<Grouping> {
#private;
experimentalNISSPlaceholder?: Pause;
constructor(algSource: FlexibleAlgSource, amount?: number);
isIdentical(other: Comparable): boolean;
get alg(): Alg;
get amount(): number;
/** @deprecated */
get experimentalRepetitionSuffix(): string;
invert(): Grouping;
experimentalExpand(iterDir?: IterationDirection, depth?: number): Generator<AlgLeaf>;
static fromString(): Grouping;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
experimentalAsSquare1Tuple(): [moveU: Move, moveD: Move] | null;
}
/** @category Alg Nodes */
declare class LineComment extends AlgCommon<LineComment> {
#private;
constructor(commentText: string);
get text(): string;
isIdentical(other: Comparable): boolean;
invert(): LineComment;
experimentalExpand(_iterDir?: IterationDirection, _depth?: number): Generator<AlgLeaf>;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
}
/** @category Alg Nodes */
declare class Commutator extends AlgCommon<Commutator> {
#private;
constructor(aSource: FlexibleAlgSource, bSource: FlexibleAlgSource);
get A(): Alg;
get B(): Alg;
isIdentical(other: Comparable): boolean;
invert(): Commutator;
experimentalExpand(iterDir?: IterationDirection, depth?: number): Generator<AlgLeaf>;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
}
/** @category Alg Nodes */
declare class Conjugate extends AlgCommon<Conjugate> {
#private;
constructor(aSource: FlexibleAlgSource, bSource: FlexibleAlgSource);
get A(): Alg;
get B(): Alg;
isIdentical(other: Comparable): boolean;
invert(): Conjugate;
experimentalExpand(iterDir: IterationDirection, depth?: number): Generator<AlgLeaf>;
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
}
/** @category Alg Nodes */
declare class Newline extends AlgCommon<Newline> {
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
isIdentical(other: Comparable): boolean;
invert(): Newline;
experimentalExpand(_iterDir?: IterationDirection, _depth?: number): Generator<AlgLeaf>;
}
/** @category Alg Nodes */
type AlgLeaf = Move | LineComment | Newline | Pause;
/** @category Alg Nodes */
type AlgBranch = Grouping | Conjugate | Commutator;
/** @category Alg Nodes */
type AlgNode = AlgLeaf | AlgBranch;
declare const DEFAULT_DIRECTIONAL = "any-direction";
type QuantumDirectionalCancellation = typeof DEFAULT_DIRECTIONAL | "same-direction" | "none";
type ModWrap = "none" | "gravity" | "canonical-centered" | "canonical-positive" | "preserve-sign";
interface AppendCancelOptions {
directional?: QuantumDirectionalCancellation;
puzzleSpecificModWrap?: ModWrap;
}
interface AppendOptions {
cancel?: boolean | AppendCancelOptions;
puzzleLoader?: {
puzzleSpecificSimplifyOptions?: PuzzleSpecificSimplifyOptions;
};
puzzleSpecificSimplifyOptions?: PuzzleSpecificSimplifyOptions;
}
interface SimplifyOptions extends AppendOptions {
depth?: number | null;
}
interface PuzzleSpecificAxisSimplifyInfo {
areQuantumMovesSameAxis: (quantumMove1: QuantumMove, quantumMove2: QuantumMove) => boolean;
simplifySameAxisMoves: (moves: Move[], quantumMod: boolean) => Move[];
}
interface PuzzleSpecificSimplifyOptions {
quantumMoveOrder?: (quantumMove: QuantumMove) => number;
axis?: PuzzleSpecificAxisSimplifyInfo;
}
type FlexibleAlgSource = string | Iterable<AlgNode> | Alg;
/**
* `Alg` is a class that encapsulates a structured alg. To create an `Alg` from a string, use:
*
* new Alg("R U R'"); // Convenient
* Alg.fromString(dynamicString); // Recommended when the string input is user-provided.
*
* Once you have an `Alg`, you can call methods to transform it:
*
* new Alg("[[R: U], R U R2']").expand().experimentalSimplify({cancel: true}).invert().log()
*
* To convert an `Alg` to a string, use .toString():
*
* new Alg("R U F").invert().toString();
*
* If you need to debug, you may also find it convenient to use .log():
*
* if (alg.isIdentical(alg.invert())) {
* alg.log("A self-inverse!")
* }
*
* For more information, see: {@link https://js.cubing.net/cubing/alg/}
*
* @category Alg
*/
declare class Alg extends AlgCommon<Alg> {
#private;
constructor(alg?: FlexibleAlgSource);
/**
* Checks whether this Alg is structurally identical to another Alg. This
* essentially means that they are written identically apart from whitespace.
*
* const alg1 = new Alg("R U L'");
* const alg2 = new Alg("L U' R'").invert();
* // true
* alg1.isIdentical(alg2);
*
* // false
* new Alg("[R, U]").isIdentical(new Alg("R U R' U'"));
* // true
* new Alg("[R, U]").expand().isIdentical(new Alg("R U R' U'"));
*
* Note that .isIdentical() efficiently compares algorithms, but mainly exists
* to help optimize code when the structure of an algorithm hasn't changed.
* There are many ways to write the "same" alg on most puzzles, but is
* *highly* recommended to avoid expanding two Alg instances to compare them,
* since that can easily slow your program to a crawl if someone inputs an alg
* containing a large repetition. In general, you should use `cubing/kpuzzle`
* to compare if two algs have the same effect on a puzzle.
*
* Also note that parser annotations are not taken into account while comparing
* algs:
*
* const alg = new Alg([new Move("R"), new Move("U2")]);
* // true, even though one of the algs has parser annotations
* alg.isIdentical(new Alg("R U2"))
*
*/
isIdentical(other: Comparable): boolean;
/**
* Returns the inverse of the given alg.
*
* Note that that this does not make any assumptions about what puzzle the alg
* is for. For example, U2 is its own inverse on a cube, but U2' has the same
* effect U3 (and not U2) on Megaminx:
*
* // Outputs: R U2' L'
* new Alg("L U2 R'").invert().log();
*/
invert(): Alg;
/** @deprecated Use {@link Alg.expand} instead. */
experimentalExpand(iterDir?: IterationDirection, depth?: number): Generator<AlgLeaf>;
/**
* Expands all Grouping, Commutator, and Conjugate parts nested inside the
* alg.
*
* // F R U R' U' F'
* new Alg("[F: [R, U]]").expand().log();
*
* // F [R, U] F'
* new Alg("[F: [R, U]]").expand(({ depth: 1 }).log();
*
* Avoid calling this on a user-provided alg unless the user explicitly asks
* to see the expanded alg. Otherwise, it's easy to make your program freeze
* when someone passes in an alg like: (R U)10000000
*
* Generally, if you want to perform an operation on an entire alg, you'll
* want to use something based on the `Traversal` mechanism, like countMoves()
* from `cubing/notation`.
*/
expand(options?: {
depth?: number;
}): Alg;
/** @deprecated */
experimentalLeafMoves(): Generator<Move>;
concat(input: FlexibleAlgSource): Alg;
/** @deprecated */
experimentalIsEmpty(): boolean;
static fromString(s: string): Alg;
/** @deprecated */
units(): Generator<AlgNode>;
childAlgNodes(): Generator<AlgNode>;
/** @deprecated */
experimentalNumUnits(): number;
experimentalNumChildAlgNodes(): number;
/** @deprecated */
get type(): string;
/**
* Converts the Alg to a string:
*
* const alg = new Alg([new Move("R"), new Move("U2"), new Move("L")])
* // R U2 L
* console.log(alg.toString())
*/
toString(experimentalSerializationOptions?: ExperimentalSerializationOptions): string;
/**
* `experimentalSimplify` can perform several mostly-syntactic simplifications on an alg:
*
* // Logs: R' U3
* import { Alg } from "@vscubing/cubing/alg";
* new Alg("R R2' U U2").experimentalSimplify({ cancel: true }).log()
*
* You can pass in a `PuzzleLoader` (currently only for 3x3x3) for puzzle-specific simplifications:
*
* // Logs: R' U'
* import { Alg } from "@vscubing/cubing/alg";
* import { cube3x3x3 } from "@vscubing/cubing/puzzles";
* new Alg("R R2' U U2").experimentalSimplify({ cancel: true, puzzleLoader: cube3x3x3 }).log()
*
* You can also cancel only moves that are in the same direction:
*
* // Logs: R R2' U'
* import { Alg } from "@vscubing/cubing/alg";
* import { cube3x3x3 } from "@vscubing/cubing/puzzles";
* new Alg("R R2' U U2").experimentalSimplify({
* cancel: { directional: "same-direction" },
* puzzleLoader: cube3x3x3
* }).log()
*
* Additionally, you can specify how moves are "wrapped":
*
* import { Alg } from "@vscubing/cubing/alg";
* import { cube3x3x3 } from "@vscubing/cubing/puzzles";
*
* function example(puzzleSpecificModWrap) {
* alg.experimentalSimplify({
* cancel: { puzzleSpecificModWrap },
* puzzleLoader: cube3x3x3
* }).log()
* }
*
* const alg = new Alg("R7' . R6' . R5' . R6")
* example("none") // R7' . R6' . R5' . R6
* example("gravity") // R . R2' . R' . R2
* example("canonical-centered") // R . R2 . R' . R2
* example("canonical-positive") // R . R2 . R3 . R2
* example("preserve-sign") // R3' . R2' . R' . R2
*
* Same-axis and simultaneous move canonicalization is not implemented yet:
*
* // Logs: R L R
* import { Alg } from "@vscubing/cubing/alg";
* import { cube3x3x3 } from "@vscubing/cubing/puzzles";
* new Alg("R L R").experimentalSimplify({ cancel: true, puzzleLoader: cube3x3x3 }).log()
*/
experimentalSimplify(options?: SimplifyOptions): Alg;
/** @deprecated See {@link experimentalSimplify} */
simplify(options?: SimplifyOptions): Alg;
}
interface NotationMapper {
notationToInternal(move: Move): Move | null;
notationToExternal(move: Move): Move | null;
}
declare function parseOptions(argv: string[]): {
puzzleDescription: PuzzleDescription | null;
options: PuzzleGeometryOptions;
};
type FaceName = string;
type OrientationDirection = [number, number, number];
type FaceBasedOrientationDescription = [
[
FaceName,
OrientationDirection
],
[
FaceName,
OrientationDirection
]
];
type BaseFaceCount = 4 | 6 | 8 | 12 | 20;
type FaceBasedOrientationDescriptionLookup = Record<BaseFaceCount, FaceBasedOrientationDescription>;
declare class PuzzleGeometryFullOptions {
verbosity: number;
allMoves: boolean;
outerBlockMoves: boolean;
vertexMoves: boolean;
addRotations: boolean;
moveList: string[] | null;
fixedOrientation: boolean;
fixedPieceType: null | "e" | "v" | "f";
orientCenters: boolean;
includeCornerOrbits: boolean;
includeCenterOrbits: boolean;
includeEdgeOrbits: boolean;
excludeOrbits: string[];
optimizeOrbits: boolean;
grayCorners: boolean;
grayCenters: boolean;
grayEdges: boolean;
puzzleOrientation: FaceBasedOrientationDescription | null;
puzzleOrientations: FaceBasedOrientationDescriptionLookup | null;
scrambleAmount: number;
constructor(options?: PuzzleGeometryOptions);
}
type PuzzleGeometryOptions = Partial<PuzzleGeometryFullOptions>;
declare class Perm {
n: number;
p: number[];
constructor(a: number[]);
toString(): string;
mul(p2: Perm): Perm;
rmul(p2: Perm): Perm;
inv(): Perm;
compareTo(p2: Perm): number;
toGap(): string;
toMathematica(): string;
order(): number;
}
declare class KTransformation {
#private;
readonly kpuzzle: KPuzzle;
readonly transformationData: KTransformationData;
constructor(kpuzzle: KPuzzle, transformationData: KTransformationData);
toJSON(): any;
invert(): KTransformation;
isIdentityTransformation(): boolean;
/** @deprecated */
static experimentalConstructIdentity(kpuzzle: KPuzzle): KTransformation;
isIdentical(t2: KTransformation): boolean;
/** @deprecated */
apply(source: KTransformationSource): KTransformation;
applyTransformation(t2: KTransformation): KTransformation;
applyMove(move: Move | string): KTransformation;
applyAlg(alg: Alg | string): KTransformation;
toKPattern(): KPattern;
repetitionOrder(): number;
selfMultiply(amount: number): KTransformation;
}
declare class KPattern {
readonly kpuzzle: KPuzzle;
readonly patternData: KPatternData;
constructor(kpuzzle: KPuzzle, patternData: KPatternData);
toJSON(): any;
static fromTransformation(transformation: KTransformation): KPattern;
/** @deprecated */
apply(source: KTransformationSource): KPattern;
applyTransformation(transformation: KTransformation): KPattern;
applyMove(move: Move | string): KPattern;
applyAlg(alg: Alg | string): KPattern;
isIdentical(other: KPattern): boolean;
/** @deprecated */
experimentalToTransformation(): KTransformation | null;
experimentalIsSolved(options: {
ignorePuzzleOrientation: boolean;
ignoreCenterOrientation: boolean;
}): boolean;
}
type KPatternData = Record<string, KPatternOrbitData>;
interface KPatternOrbitData {
pieces: number[];
orientation: number[];
/** Each piece may have an "orientation mod" that means "the orientation of
* this piece is known mod [value]".
*
* Suppose `.numOrientations` for this orbit has a value of N. This is
* considered the default value for the orientation mod of each piece in the
* orbit.
*
* - Each entry must be one of the following:
* - A proper divisor of N.
* - For example: if N is 12, then one of: 1, 2, 3, 6
* - The special value 0, indicating the default value (N).
* - This indicates that the orientation of a piece is fully known, i.e.
* that its "orientation mod" is the default value (N). However, such a
* value is recorded as 0 instead of N, in order to make it simpler to
* implement and debug pattern logic involving the default value.
* - If `.orientationMod[i]` is a proper divisor of N (i.e. not 0), then
* `.orientation[i]` must be less than `.orientationMod[i]`. That is, the
* orientation values must be individually "normalized" for each piece.
* - If the `orientationMod` field is not present, then every piece is
* considered to have the default value for its "orientation mod".
*
* For a "real-world" example of this concept, consider a traditional analog
* 12-hour clock dial, like one that might hang on the wall in a school room.
* Although there are 24 hours in a day, A.M. and P.M. times are not
* distinguishable on such a clock. Since 3:00 (AM) and 15:00 are not
* distinguishable, we would read either of those times as 3:00 with an
* implicit "orientation mod" of 12.
*
* For most puzzles, however, we care about "visual" indistinguishability
* rather than "temporal" indistinguishability. To adapt the previous example,
* imagine a 24-hour clock with 24 hour marks around the dial, but where the
* hour hand is symmetric and points equally at the current hour as well as
* its diametic opposite (like a compass needle but painted all in one color).
* This has the same set of "valid patterns" as a normal 12-hour clock. Such a
* clock also has an "orientation mod" of 12, but where the multiples of the
* modulus have been "unfolded" to show their full symmetry instead of being
* implicit.
*
* For a non-trivial puzzle example, consider Eitan's FisherTwist, a shape mod
* of the 3x3x3 cube:
* https://www.hknowstore.com/locale/en-US/item.aspx?corpname=nowstore&itemid=97eb4e89-367e-4d02-b7f0-34e5e7f3cd12
*
* - The 4 equatorial centers have C₂ symmetry — it is possible to rotate any
* of these centers 180° without a visible change to the state. This means
* that the possible orientations "loop" after incrementing the orientation
* by 2 (two turns clockwise), and therefore the "orientation mod" of a
* given piece is only 2.
* - If we apply a counter-clockwise rotation to one of these centers, the
* transformation applies an orientation of 3. But the net orientation is
* recorded as a normalized value of 1 instead, because 3 (mod 2) ≡ 1 (mod
* 2).
* - The 2 polar centers (U and D) have no distinguishable rotations. This
* means that their orientation is "known mod 1" — any transformation of one
* of these centers is indistinguishable from another transformation of the
* same center, and all of them are mapped to a value of 0 (the only
* possible value that exists mod 1).
*
* For 3x3x3:
*
* - When solving a normal 3x3x3, center orientations are conventionally
* ignored. This is similar to the polar center case for Eitan's
* FisherTwist, and the "orientation mod" of each piece is 1. This is also
* the core motivating use case.
* - For a supercube
* (https://experiments.cubing.net/cubing.js/twisty/supercube.html) or the
* general case of a "picture cube", all four center orientations are
* distinguishable for every center. This means all centers have the default
* orientation mod of 4. As documented above, this can be recorded with a
* `.orientationMod` of `[0, 0, 0, 0, 0, 0]`, or equivalently by omitting
* the `.orientationMod` field.
* - When modeling a real 3x3x3 speedcube, it is common to have a logo on a
* single sticker. If you want to model the exact visually distinguishable
* states of such a puzzles, it is possible to use an `.orientationMod` such
* as `[0, 1, 1, 1, 1, 1]`. For example, this can make it easy to find an
* alg for a given case "while keeping the logo the same", without placing
* more restrictions on other centers (which could make the search slower or
* produce longer solutions).
*
* For those with a mathematical background, you may notice a relationship to
* the concept of a coset (https://en.wikipedia.org/wiki/Coset). For example,
* consider the group of patterns of a `KPuzzle` (without indistinguishable
* pieces) generated by a set of transformations. We can assign each set of
* piece orbits an orientation mod value (which must be identical for all
* constituent pieces of the same orbit). Each such choice generates a set of
* valid `KPattern`s that forms a subgroup, and each set of valid `.orientation`
* values defines one coset of this set. However, note that the set of valid
* `KPattern`s does *not* form a group when there are any pieces with different
* `.orientationMod` values that share an orbit.
*
* --------
*
* Note that the concept of "orientation mod" exclusively applies to `KPattern`,
* not `KTransformation`. If we tried to apply the orientation mod
* calculations to the *transformations* of Eitan's FisherTwist, then `SWAP =
* [U, M' E2 M]` would be indistinguishable from the identity. This would mean
* that if we calculated `SWAP` and then used this calculation for `S SWAP
* S'`, then we would conclude that it has no net effect. However, `S SWAP S'`
* does *not* have the same effect as doing nothing — it visibly rotates the L
* and R centers! (In mathematical terms: the set of `KTransformation`s would
* not form a valid set of semigroup actions, due to broken associativity.)
*
* Although there are times that we could theoretically save some time/space
* by ignoring some information when it's not needed for working with certain
* `KTransformation`s (e.g. ignoring all center orientations for 3x3x3), it is
* more practical for each `KTransformation` to always track the full range
* for each piece's `.orientation`. For example:
*
* - This is simpler, both conceptually and in code.
* - This allows changing the set of moves for a puzzle, without recalculating
* cached transformations or certain lookup tables (useful for alg
* searches).
* - This allows swapping out a normal 3x3x3 in a `<twisty-player>` for a
* picture cube, without re-calculating the center orientations of the
* current alg.
*
* These use cases may not be strictly "necessary", but the opposite behaviour
* might be surprising or frustrating if someone does not expect it. So we
* implement it this way.
*
* Informally, the `KTransformation` has the full responsibility for tracking
* "what really happens" — even if the effect is invisible in some cases,
* while the `KPattern` tracks both what "is" and what "isn't" known.
**/
orientationMod?: number[];
}
type KTransformationData = Record<string, KTransformationOrbitData>;
interface KTransformationOrbitData {
permutation: number[];
orientationDelta: number[];
}
interface KPuzzleOrbitDefinition {
orbitName: string;
numPieces: number;
numOrientations: number;
}
interface KPuzzleDefinition {
name: string;
orbits: KPuzzleOrbitDefinition[];
defaultPattern: KPatternData;
moves: Record<string, KTransformationData>;
derivedMoves?: Record<string, string>;
experimentalIsPatternSolved?: (kpattern: KPattern, options: {
ignorePuzzleOrientation: boolean;
ignoreCenterOrientation: boolean;
}) => boolean;
}
declare class PGOrbitDef {
size: number;
mod: number;
constructor(size: number, mod: number);
reassemblySize(): bigint;
}
declare class PGOrbitsDef {
orbitnames: string[];
private orbitdefs;
solved: VisibleState;
movenames: string[];
moveops: PGTransform[];
isRotation: boolean[];
forcenames: boolean[];
constructor(orbitnames: string[], orbitdefs: PGOrbitDef[], solved: VisibleState, movenames: string[], moveops: PGTransform[], isRotation: boolean[], forcenames: boolean[]);
toKTransformationData(t: PGTransform): KTransformationData;
toKPatternData(t: PGTransform): KPatternData;
static transformToKTransformationData(orbitnames: string[], t: PGTransform): KTransformationData;
private describeSet;
toKsolve(name: string, mapper?: NotationMapper): string[];
toKPuzzleDefinition(includemoves: boolean): KPuzzleDefinition;
optimize(): PGOrbitsDef;
scramble(n: number): void;
getScrambleTransformation(n: number): PGTransform;
reassemblySize(): bigint;
}
declare class PGOrbit {
perm: number[];
ori: number[];
orimod: number;
private static ktransformationCache;
static e(n: number, mod: number): PGOrbit;
constructor(perm: number[], ori: number[], orimod: number);
mul(b: PGOrbit): PGOrbit;
inv(): PGOrbit;
equal(b: PGOrbit): boolean;
killOri(): this;
toPerm(): Perm;
identicalPieces(): number[][];
order(): number;
isIdentity(): boolean;
private zeroOris;
remap(no: number[], on: number[], nv: number): PGOrbit;
remapVS(no: number[], nv: number): PGOrbit;
appendDefinition(result: string[], name: string, useVS: boolean, concise?: boolean): void;
toKTransformationOrbitData(): KTransformationOrbitData;
toKPatternOrbitData(): KPatternOrbitData;
}
declare class PGTransformBase {
orbits: PGOrbit[];
constructor(orbits: PGOrbit[]);
internalMul(b: PGTransformBase): PGOrbit[];
protected internalInv(): PGOrbit[];
equal(b: PGTransformBase): boolean;
protected killOri(): this;
toPerm(): Perm;
identicalPieces(): number[][];
order(): number;
}
declare class PGTransform extends PGTransformBase {
mul(b: PGTransform): PGTransform;
mulScalar(n: number): PGTransform;
inv(): PGTransform;
e(): PGTransform;
}
declare class VisibleState extends PGTransformBase {
mul(b: PGTransform): VisibleState;
}
type PuzzleDescriptionString = string;
declare const PGPuzzles: {
[name: string]: PuzzleDescriptionString;
};
type PuzzleName = keyof typeof PGPuzzles;
declare class Quat {
a: number;
b: number;
c: number;
d: number;
constructor(a: number, b: number, c: number, d: number);
mul(q: Quat): Quat;
toString(): string;
dist(q: Quat): number;
len(): number;
cross(q: Quat): Quat;
dot(q: Quat): number;
normalize(): Quat;
makenormal(): Quat;
normalizeplane(): Quat;
smul(m: number): Quat;
sum(q: Quat): Quat;
sub(q: Quat): Quat;
angle(): number;
invrot(): Quat;
det3x3(a00: number, a01: number, a02: number, a10: number, a11: number, a12: number, a20: number, a21: number, a22: number): number;
rotateplane(q: Quat): Quat;
orthogonal(): Quat;
pointrotation(b: Quat): Quat;
unproject(b: Quat): Quat;
rotatepoint(q: Quat): Quat;
rotateface(face: Quat[]): Quat[];
intersect3(p2: Quat, p3: Quat): Quat | false;
side(x: number): number;
/**
* Cuts a face by this plane, or returns null if there
* is no intersection.
* @param face The face to cut.
*/
cutface(face: Quat[]): Quat[][] | null;
cutfaces(faces: Quat[][]): Quat[][];
faceside(face: Quat[]): number;
sameplane(p: Quat): boolean;
makecut(r: number): Quat;
}
interface TextureMapper {
getuv(fn: number, threed: number[]): number[];
}
interface StickerDatSticker {
coords: number[];
color: string;
orbit: string;
ord: number;
ori: number;
face: number;
isDup?: boolean;
}
interface StickerDatFace {
coords: number[];
name: string;
}
type StickerDatAxis = {
coordinates: number[];
quantumMove: Move;
order: number;
};
interface StickerDat {
stickers: StickerDatSticker[];
faces: StickerDatFace[];
axis: StickerDatAxis[];
unswizzle(mv: Move): Move | null;
notationMapper: NotationMapper;
textureMapper: TextureMapper;
}
declare function getPG3DNamedPuzzles(): {
[s: string]: PuzzleDescriptionString;
};
declare function getPuzzleDescriptionString(puzzleName: PuzzleName): PuzzleDescriptionString;
declare const PUZZLE_BASE_SHAPES: readonly ["c", "t", "o", "d", "i"];
type PuzzleBaseShape = (typeof PUZZLE_BASE_SHAPES)[number];
declare const PUZZLE_CUT_TYPES: readonly ["f", "v", "e"];
type PuzzleCutType = (typeof PUZZLE_CUT_TYPES)[number];
type PuzzleCutDescription = {
cutType: PuzzleCutType;
distance: number;
};
type PuzzleDescription = {
shape: PuzzleBaseShape;
cuts: PuzzleCutDescription[];
};
declare function parsePuzzleDescription(s: PuzzleDescriptionString): PuzzleDescription | null;
declare function getPuzzleGeometryByDesc(desc: string, options?: PuzzleGeometryOptions): PuzzleGeometry;
declare function getPuzzleGeometryByName(puzzleName: PuzzleName, options?: PuzzleGeometryOptions): PuzzleGeometry;
type MoveSetGeo = [string, string, string, string, number];
/** @category PuzzleGeometry */
declare class PuzzleGeometry {
puzzleDescription: PuzzleDescription;
private rotations;
baseplanerot: Quat[];
private baseplanes;
private facenames;
private faceplanes;
private edgenames;
private vertexnames;
private geonormals;
private moveplanes;
private moveplanes2;
moveplanesets: Quat[][];
private moveplanenormals;
movesetorders: number[];
movesetgeos: MoveSetGeo[];
private basefaces;
private faces;
private facecentermass;
private baseFaceCount;
stickersperface: number;
shortedge: number;
private markedface;
cubies: number[][];
private vertexdistance;
private edgedistance;
private facetocubie;
private facetoord;
private moverotations;
private facelisthash;
private cubiesetnames;
private cubieords;
private cubiesetnums;
private cubieordnums;
private orbitoris;
private cubievaluemap;
private cubiesetcubies;
cmovesbyslice: number[][][];
parsedmovelist: [
string | undefined,
number,
number,
number,
boolean,
number
][];
private duplicatedFaces;
private duplicatedCubies;
private fixedCubie;
private net;
private colors;
private swizzler;
notationMapper: NotationMapper;
private addNotationMapper;
private setReidOrSpeffzOrder;
private options;
constructor(puzzleDescription: PuzzleDescription, options: PuzzleGeometryOptions);
create(puzzleDescription: PuzzleDescription): void;
private keyface;
private keyface2;
private keyface3;
private findface;
private project2d;
private upperStringToBitSet;
allstickers(): void;
unswizzle(mv: Move): Move | null;
private stringToBlockMove;
parseMove(move: Move): [string | undefined, number, number, number, boolean, number];
private parsemove;
genperms(): void;
private getboundarygeometry;
private getmovesets;
private graybyori;
private skipbyori;
private skipcubie;
private header;
writegap(): string;
writemathematica(): string;
writeksolve(name?: string): string;
getKPuzzleDefinition(fortwisty?: boolean, includemoves?: boolean): KPuzzleDefinition;
getMoveFromBits(moverange: number[], amount: number, inverted: boolean, axiscmoves: number[][], setmoves: number[] | undefined, movesetorder: number): PGTransform;
private omitSet;
private diffmvsets;
getOrbitsDef(fortwisty: boolean, includemoves?: boolean): PGOrbitsDef;
getScramble(n?: number): KTransformationData;
getMovesAsPerms(): Perm[];
showcanon(disp: (s: string) => void): void;
getsolved(): Perm;
private getOrientationRotation;
private getInitial3DRotation;
private generate2dmapping;
generatesvg(w?: number, h?: number, trim?: number, threed?: boolean): string;
get3d(options?: {
stickerColors?: string[];
darkIgnoredOrbits?: boolean;
}): StickerDat;
getGeoNormal(geoname: string): number[] | undefined;
private getfaceindex;
textForTwizzleExplorer(): string;
writeSchreierSims(tw: (s: string) => void): void;
}
declare class PGNotation {
private pg;
private orbitNames;
constructor(pg: PuzzleGeometry, od: PGOrbitsDef);
lookupMove(move: Move): KTransformationData | null;
remapKPuzzleDefinition(kpuzzleDefinition: KPuzzleDefinition): KPuzzleDefinition;
}
type KTransformationSource = Alg | Move | string | KTransformation;
declare class KPuzzle {
#private;
readonly definition: KPuzzleDefinition;
private experimentalPGNotation;
constructor(definition: KPuzzleDefinition, options?: {
experimentalPGNotation?: PGNotation;
});
lookupOrbitDefinition(orbitName: string): KPuzzleOrbitDefinition;
name(): string;
identityTransformation(): KTransformation;
moveToTransformation(move: Move | string): KTransformation;
algToTransformation(alg: Alg | string): KTransformation;
/** @deprecated */
toTransformation(source: KTransformationSource): KTransformation;
defaultPattern(): KPattern;
canConvertDefaultPatternToUniqueTransformation(): boolean;
}
declare const experimentalStickerings: Record<string, {
groups?: Partial<Record<PuzzleID, string>>;
}>;
type FaceletMeshStickeringMask = "regular" | "dim" | "oriented" | "experimentalOriented2" | "ignored" | "invisible" | "mystery";
type FaceletStickeringMask = {
mask: FaceletMeshStickeringMask;
hintMask?: FaceletMeshStickeringMask;
};
type PieceStickeringMask = {
facelets: (FaceletMeshStickeringMask | FaceletStickeringMask | null)[];
};
type OrbitStickeringMask = {
pieces: (PieceStickeringMask | null)[];
};
type StickeringMask = {
specialBehaviour?: "picture";
name?: string;
orbits: Record<string, OrbitStickeringMask>;
};
type MillisecondTimestamp = number;
type Duration = MillisecondTimestamp;
type Timestamp = MillisecondTimestamp;
declare enum Direction {
Forwards = 1,
Paused = 0,
Backwards = -1
}
interface MoveInProgress {
move: Move;
direction: Direction;
fraction: number;
}
type PuzzlePosition = {
pattern: KPattern;
movesInProgress: MoveInProgress[];
};
declare enum BoundaryType {
Move = "move",
EntireTimeline = "entire-timeline"
}
interface TimeRange {
start: MillisecondTimestamp;
end: MillisecondTimestamp;
}
interface UserVisibleError {
errors: string[];
}
declare class UserVisibleErrorTracker extends SimpleTwistyPropSource<UserVisibleError> {
getDefaultValue(): UserVisibleError;
reset(): void;
protected canReuseValue(_v1: UserVisibleError, _v2: UserVisibleError): boolean;
}
type InputRecord = Record<string, any>;
type InputProps<T extends InputRecord> = {
[s in keyof T]: TwistyPropParent<T[s]>;
};
interface SourceEventDetail<OutputType> {
sourceProp: TwistyPropSource<OutputType, any>;
value: Promise<OutputType>;
generation: number;
}
type SourceEvent<T> = CustomEvent<SourceEventDetail<T>>;
type PromiseOrValue<T> = T | Promise<T>;
declare abstract class TwistyPropParent<T> {
#private;
abstract get(): Promise<T>;
canReuse(v1: T, v2: T): boolean;
protected canReuseValue(_v1: T, _v2: T): boolean;
debugGetChildren(): TwistyPropDerived<any, any>[];
protected addChild(child: TwistyPropDerived<any, any>): void;
protected removeChild(child: TwistyPropDerived<any, any>): void;
protected lastSourceGeneration: number;
protected markStale(sourceEvent: SourceEvent<any>): void;
/** @deprecated */
addRawListener(listener: () => void, options?: {
initial: boolean;
}): void;
/** @deprecated */
removeRawListener(listener: () => void): void;
addFreshListener(listener: (value: T) => void): void;
removeFreshListener(listener: (value: T) => void): void;
}
declare abstract class TwistyPropSource<OutputType, InputType = OutputType> extends TwistyPropParent<OutputType> {
#private;
abstract getDefaultValue(): PromiseOrValue<OutputType>;
constructor(initialValue?: PromiseOrValue<InputType>);
set(input: PromiseOrValue<InputType>): void;
get(): Promise<OutputType>;
protected deriveFromPromiseOrValue(input: PromiseOrValue<InputType>, oldValuePromise: Promise<OutputType>): Promise<OutputType>;
protected abstract derive(input: InputType, oldValuePromise: Promise<OutputType>): PromiseOrValue<OutputType>;
}
declare abstract class SimpleTwistyPropSource<SimpleType> extends TwistyPropSource<SimpleType> {
protected derive(input: SimpleType): PromiseOrValue<SimpleType>;
}
declare const NO_VALUE: unique symbol;
type NoValueType = typeof NO_VALUE;
declare abstract class TwistyPropDerived<InputTypes extends InputRecord, OutputType> extends TwistyPropParent<OutputType> {
#private;
protected userVisibleErrorTracker?: UserVisibleErrorTracker | undefined;
constructor(parents: InputProps<InputTypes>, userVisibleErrorTracker?: UserVisibleErrorTracker | undefined);
get(): Promise<OutputType>;
protected abstract derive(input: InputTypes): PromiseOrValue<OutputType>;
}
type SimpleDirection = Direction.Forwards | Direction.Backwards;
interface PlayingInfo {
playing: boolean;
direction: SimpleDirection;
untilBoundary: BoundaryType;
loop: boolean;
}
declare class PlayingInfoProp extends TwistyPropSource<PlayingInfo, Partial<PlayingInfo>> {
getDefaultValue(): Promise<PlayingInfo>;
protected derive(newInfo: Partial<PlayingInfo>, oldValuePromise: Promise<PlayingInfo>): Promise<PlayingInfo>;
protected canReuseValue(v1: PlayingInfo, v2: PlayingInfo): boolean;
}
declare class ArbitraryStringProp extends SimpleTwistyPropSource<string | null> {
getDefaultValue(): string | null;
}
declare class URLProp extends TwistyPropSource<URL | null, URL | string | null> {
getDefaultValue(): URL | null;
derive(input: URL | string | null): URL | null;
}
declare class AlgIssues {
readonly warnings: readonly string[];
readonly errors: readonly string[];
constructor(issues?: {
warnings?: string[];
errors?: string[];
});
add(issues?: {
warnings?: string[];
errors?: string[];
}): AlgIssues;
/** @deprecated */
log(): void;
}
interface AlgWithIssues {
alg: Alg;
issues: AlgIssues;
}
declare class AlgProp extends TwistyPropSource<AlgWithIssues, Alg | string> {
getDefaultValue(): AlgWithIssues;
protected canReuseValue(v1: AlgWithIssues, v2: AlgWithIssues): boolean;
protected derive(newAlg: Alg | string): Promise<AlgWithIssues>;
}
type AlgTransformationPropInputs = {
setupAlg: AlgWithIssues;
kpuzzle: KPuzzle;
};
declare class AlgTransformationProp extends TwistyPropDerived<AlgTransformationPropInputs, KTransformation> {
derive(input: AlgTransformationPropInputs): KTransformation;
}
type AnimatedLeafAlgNode = Move | Pause;
interface CurrentMove {
move: Move;
direction: Direction;
fraction: number;
startTimestamp: MillisecondTimestamp;
endTimestamp: MillisecondTimestamp;
}
interface CurrentMoveInfo {
patternIndex: number;
currentMoves: CurrentMove[];
movesFinishing: CurrentMove[];
movesFinished: CurrentMove[];
movesStarting: CurrentMove[];
latestStart: number;
earliestEnd: number;
}
interface AlgIndexer {
getAnimLeaf(index: number): AnimatedLeafAlgNode | null;
indexToMoveStartTimestamp(index: number): Timestamp;
patternAtIndex(index: number, startPattern?: KPattern): KPattern;
transformationAtIndex(index: number): KTransformation;
numAnimatedLeaves(): number;
timestampToIndex(timestamp: Timestamp): number;
algDuration(): Duration;
moveDuration(index: number): number;
timestampToPosition?: (timestamp: Timestamp, startPattern?: KPattern) => PuzzlePosition;
currentMoveInfo?: (timestamp: Timestamp) => CurrentMoveInfo;
}
declare const setupToLocations: {
start: boolean;
end: boolean;
};
type SetupToLocation = keyof typeof setupToLocations;
declare class SetupAnchorProp extends SimpleTwistyPropSource<SetupToLocation> {
getDefaultValue(): SetupToLocation;
}
interface AnchorTransformationPropInputs {
setupTransformation: KTransformation | null;
setupAnchor: SetupToLocation;
setupAlgTransformation: KTransformation;
indexer: AlgIndexer;
}
declare class AnchorTransformationProp extends TwistyPropDerived<AnchorTransformationPropInputs, KTransformation> {
derive(inputs: AnchorTransformationPropInputs): KTransformation;
}
interface AnimationTimelineLeaf {
animLeaf: AlgLeaf;
start: number;
end: number;
}
type AnimationTimelineLeaves = AnimationTimelineLeaf[];
declare class AnimationTimelineLeavesRequestProp extends SimpleTwistyPropSource<AnimationTimelineLeaf[] | null> {
getDefaultValue(): AnimationTimelineLeaf[] | null;
}
interface CatchUpMove {
move: Move | null;
amount: number;
}
declare class CatchUpMoveProp extends SimpleTwistyPropSource<CatchUpMove> {
getDefaultValue(): CatchUpMove;
protected canReuseValue(v1: CatchUpMove, v2: CatchUpMove): boolean;
}
interface CurrentLeavesSimplifiedPropInputs {
currentMoveInfo: CurrentMoveInfo;
}
interface CurrentLeavesSimplified {
patternIndex: number;
movesFinishing: Move[];
movesFinished: Move[];
}
declare class CurrentLeavesSimplifiedProp extends TwistyPropDerived<CurrentLeavesSimplifiedPropInputs, CurrentLeavesSimplified> {
protected derive(inputs: CurrentLeavesSimplifiedPropInputs): CurrentLeavesSimplified;
protected canReuseValue(v1: CurrentLeavesSimplified, v2: CurrentLeavesSimplified): boolean;
}
declare const smartTimestamps: {
auto: boolean;
start: boolean;
end: boolean;
anchor: boolean;
"opposite-anchor": boolean;
};
type TimestampRequest = MillisecondTimestamp | keyof typeof smartTimestamps;
declare class TimestampRequestProp extends SimpleTwistyPropSource<TimestampRequest> {
getDefaultValue(): TimestampRequest;
set(v: PromiseOrValue<TimestampRequest>): void;
protected validInput(v: TimestampRequest): boolean;
}
interface DetailedTimelineInfoInputs {
timestampRequest: TimestampRequest;
timeRange: TimeRange;
setupAnchor: SetupToLocation;
setupAlg: AlgWithIssues;
}
interface DetailedTimelineInfo {
timestamp: MillisecondTimestamp;
timeRange: TimeRange;
atStart: boolean;
atEnd: boolean;
}
declare class DetailedTimelineInfoProp extends TwistyPropDerived<DetailedTimelineInfoInputs, DetailedTimelineInfo> {
#private;
protected derive(inputs: DetailedTimelineInfoInputs): DetailedTimelineInfo;
protected canReuseValue(v1: DetailedTimelineInfo, v2: DetailedTimelineInfo): boolean;
}
interface PositionPropInputs {
indexer: AlgIndexer;
detailedTimelineInfo: DetailedTimelineInfo;
catchUpMove: CatchUpMove;
}
declare class CurrentMoveInfoProp extends TwistyPropDerived<PositionPropInputs, CurrentMoveInfo> {
derive(inputs: PositionPropInputs): CurrentMoveInfo;
}
interface CurrentTransformationPropInputs {
anchoredStart: KTransformation;
currentLeavesSimplified: CurrentLeavesSimplified;
indexer: AlgIndexer;
}
declare class CurrentPatternProp extends TwistyPropDerived<CurrentTransformationPropInputs, KPattern> {
derive(inputs: CurrentTransformationPropInputs): KPattern;
}
declare const visualizationFormats: {
readonly "3D": true;
readonly "2D": true;
readonly "experimental-2D-LL": true;
readonly "experimental-2D-LL-face": true;
readonly PG3D: true;
};
type VisualizationFormat = keyof typeof visualizationFormats;
type VisualizationFormatWithAuto = VisualizationFormat | "auto";
declare class VisualizationFormatProp extends SimpleTwistyPropSource<VisualizationFormatWithAuto> {
getDefaultValue(): VisualizationFormatWithAuto;
}
type VisualizationStrategyPropInputs = {
visualizationRequest: VisualizationFormatWithAuto;
puzzleID: PuzzleID;
};
type VisualizationStrategy = "Cube3D" | "2D" | "experimental-2D-LL" | "experimental-2D-LL-face" | "PG3D";
declare class VisualizationStrategyProp extends TwistyPropDerived<VisualizationStrategyPropInputs, VisualizationStrategy> {
derive(inputs: VisualizationStrategyPropInputs): VisualizationStrategy;
}
declare const puzzleIDs: {
"3x3x3": boolean;
custom: boolean;
"2x2x2": boolean;
"4x4x4": boolean;
"5x5x5": boolean;
"6x6x6": boolean;
"7x7x7": boolean;
"40x40x40": boolean;
megaminx: boolean;
pyraminx: boolean;
square1: boolean;
clock: boolean;
skewb: boolean;
fto: boolean;
gigaminx: boolean;
master_tetraminx: boolean;
kilominx: boolean;
redi_cube: boolean;
baby_fto: boolean;
melindas2x2x2x2: boolean;
tri_quad: boolean;
loopover: boolean;
};
type PuzzleID = keyof typeof puzzleIDs;
declare class PuzzleIDRequestProp extends SimpleTwistyPropSource<PuzzleID | NoValueType> {
getDefaultValue(): PuzzleID | NoValueType;
}
declare const indexerStrategyNames: {
auto: boolean;
simple: boolean;
tree: boolean;
simultaneous: boolean;
};
type IndexerStrategyName = keyof typeof indexerStrategyNames;
declare class IndexerConstructorRequestProp extends SimpleTwistyPropSource<IndexerStrategyName> {
getDefaultValue(): IndexerStrategyName;
}
type IndexerConstructor = new (kpuzzle: KPuzzle, alg: Alg, options?: {
animationTimelineLeaves?: AnimationTimelineLeaves | null;
}) => AlgIndexer;
interface IndexerConstructorPropInputs {
puzzle: PuzzleID;
alg: AlgWithIssues;
visualizationStrategy: VisualizationStrategy;
indexerConstructorRequest: IndexerStrategyName;
animationTimelineLeaves: AnimationTimelineLeaves | null;
}
declare class IndexerConstructorProp extends TwistyPropDerived<IndexerConstructorPropInputs, IndexerConstructor> {
derive(inputs: IndexerConstructorPropInputs): IndexerConstructor;
}
type IndexerPropInputs = {
indexerConstructor: IndexerConstructor;
algWithIssues: AlgWithIssues;
kpuzzle: KPuzzle;
animationTimelineLeaves: AnimationTimelineLeaves | null;
};
declare class IndexerProp extends TwistyPropDerived<IndexerPropInputs, AlgIndexer> {
derive(input: IndexerPropInputs): AlgIndexer;
}
interface LegacyPositionPropInputs {
currentMoveInfo: CurrentMoveInfo;
currentPattern: KPattern;
}
declare class LegacyPositionProp extends TwistyPropDerived<LegacyPositionPropInputs, PuzzlePosition> {
derive(inputs: LegacyPositionPropInputs): PuzzlePosition;
}
declare class PuzzleAlgProp extends TwistyPropDerived<{
algWithIssues: AlgWithIssues;
kpuzzle: KPuzzle;
}, AlgWithIssues> {
derive(inputs: {
algWithIssues: AlgWithIssues;
kpuzzle: KPuzzle;
}): Promise<AlgWithIssues>;
}
declare class SetupTransformationProp extends SimpleTwistyPropSource<KTransformation | null> {
getDefaultValue(): KTransformation | null;
}
declare class KPuzzleProp extends TwistyPropDerived<{
puzzleLoader: PuzzleLoader;
}, KPuzzle> {
derive(inputs: {
puzzleLoader: PuzzleLoader;
}): Promise<KPuzzle>;
}
declare class PGPuzzleDescriptionStringProp extends SimpleTwistyPropSource<PuzzleDescriptionString | NoValueType> {
getDefaultValue(): PuzzleDescriptionString | NoValueType;
}
declare class PuzzleIDProp extends TwistyPropDerived<{
puzzleLoader: PuzzleLoader;
}, PuzzleID> {
derive(inputs: {
puzzleLoader: PuzzleLoader;
}): Promise<PuzzleID>;
}
interface PuzzleLoaderPropInputs {
puzzleIDRequest: PuzzleID | NoValueType;
puzzleDescriptionRequest: PuzzleDescriptionString | NoValueType;
}
declare class PuzzleLoaderProp extends TwistyPropDerived<PuzzleLoaderPropInputs, PuzzleLoader> {
derive(inputs: PuzzleLoaderPropInputs): PuzzleLoader;
}
declare let HTMLElementShim: typeof HTMLElement;
declare class ManagedCustomElement extends HTMLElementShim {
readonly shadow: ShadowRoot;
readonly contentWrapper: HTMLDivElement;
constructor(options?: {
mode?: "open" | "closed";
});
protected addCSS(cssSource: CSSStyleSheet): void;
protected removeCSS(cssSource: CSSStyleSheet): void;
addElement<T extends Node>(element: T): T;
prependElement<T extends Node>(element: T): void;
removeElement<T extends Node>(element: T): T;
}
declare const viewerLinkPages: {
twizzle: boolean;
"experimental-twizzle-explorer": boolean;
none: boolean;
};
type ViewerLinkPage = keyof