@wodzik/cubecore 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +118 -0
- package/analyze/client.d.ts +18 -0
- package/analyze/client.js +42 -0
- package/analyze/index.d.ts +114 -0
- package/analyze/index.js +151 -0
- package/analyze/rotations.d.ts +3 -0
- package/analyze/rotations.js +15 -0
- package/analyze/roux.d.ts +67 -0
- package/analyze/roux.js +95 -0
- package/analyze/worker.d.ts +6 -0
- package/analyze/worker.js +31 -0
- package/analyze/zz.d.ts +47 -0
- package/analyze/zz.js +102 -0
- package/bld/index.d.ts +14 -0
- package/bld/index.js +14 -0
- package/bld/memo.d.ts +66 -0
- package/bld/memo.js +139 -0
- package/bld/scheme.d.ts +30 -0
- package/bld/scheme.js +35 -0
- package/bld/skin.d.ts +26 -0
- package/bld/skin.js +46 -0
- package/bld/tracker.d.ts +68 -0
- package/bld/tracker.js +145 -0
- package/bluetooth/clock.d.ts +22 -0
- package/bluetooth/clock.js +37 -0
- package/bluetooth/cubeSkins.d.ts +31 -0
- package/bluetooth/cubeSkins.js +32 -0
- package/bluetooth/gyro.d.ts +31 -0
- package/bluetooth/gyro.js +46 -0
- package/bluetooth/index.d.ts +5 -0
- package/bluetooth/index.js +5 -0
- package/bluetooth/session.d.ts +140 -0
- package/bluetooth/session.js +201 -0
- package/bluetooth/simulated.d.ts +52 -0
- package/bluetooth/simulated.js +80 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.d.ts +38 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.js +51 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.d.ts +35 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.js +70 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.d.ts +273 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.js +846 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.d.ts +38 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.js +340 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.js +118 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.js +15 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.d.ts +18 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.js +152 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.d.ts +74 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.js +161 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.d.ts +7 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.js +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.js +36 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/index.d.ts +4 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/index.js +4 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.d.ts +12 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.js +122 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.js +55 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.d.ts +21 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.js +47 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.js +24 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.js +57 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.js +67 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.d.ts +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.js +98 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.d.ts +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.js +128 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.d.ts +5 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.js +60 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.js +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.d.ts +7 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.js +142 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.js +21 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.d.ts +32 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.js +1 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.js +68 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.js +178 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.d.ts +16 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.js +141 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.js +10 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.d.ts +26 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.js +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.js +265 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.js +345 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.d.ts +15 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.js +382 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.js +352 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.d.ts +57 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.js +243 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.js +454 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.js +424 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.d.ts +78 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.js +1 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/utils.d.ts +45 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/utils.js +150 -0
- package/cfop/caseData.d.ts +16 -0
- package/cfop/caseData.js +88 -0
- package/cfop/cases.d.ts +42 -0
- package/cfop/cases.js +116 -0
- package/cfop/f2l.d.ts +28 -0
- package/cfop/f2l.js +88 -0
- package/cfop/f2lData.d.ts +15 -0
- package/cfop/f2lData.js +49 -0
- package/cfop/index.d.ts +41 -0
- package/cfop/index.js +110 -0
- package/cfop/slots.d.ts +2 -0
- package/cfop/slots.js +1 -0
- package/core/arrows.d.ts +24 -0
- package/core/arrows.js +22 -0
- package/core/bytes.d.ts +27 -0
- package/core/bytes.js +145 -0
- package/core/centers.d.ts +32 -0
- package/core/centers.js +83 -0
- package/core/checks.d.ts +53 -0
- package/core/checks.js +85 -0
- package/core/cubies.d.ts +45 -0
- package/core/cubies.js +169 -0
- package/core/frames.d.ts +61 -0
- package/core/frames.js +126 -0
- package/core/geometry.d.ts +58 -0
- package/core/geometry.js +132 -0
- package/core/index.d.ts +19 -0
- package/core/index.js +19 -0
- package/core/masks.d.ts +32 -0
- package/core/masks.js +39 -0
- package/core/method.d.ts +35 -0
- package/core/method.js +30 -0
- package/core/moves.d.ts +48 -0
- package/core/moves.js +88 -0
- package/core/notation.d.ts +48 -0
- package/core/notation.js +226 -0
- package/core/physical.d.ts +53 -0
- package/core/physical.js +99 -0
- package/core/practice.d.ts +62 -0
- package/core/practice.js +87 -0
- package/core/sequence.d.ts +90 -0
- package/core/sequence.js +157 -0
- package/core/stages.d.ts +90 -0
- package/core/stages.js +21 -0
- package/core/state.d.ts +55 -0
- package/core/state.js +140 -0
- package/core/stateCodec.d.ts +12 -0
- package/core/stateCodec.js +51 -0
- package/core/stream.d.ts +45 -0
- package/core/stream.js +75 -0
- package/core/tracker.d.ts +62 -0
- package/core/tracker.js +121 -0
- package/element/alg.d.ts +45 -0
- package/element/alg.js +150 -0
- package/element/base.d.ts +2 -0
- package/element/base.js +3 -0
- package/element/bld.d.ts +82 -0
- package/element/bld.js +222 -0
- package/element/index.d.ts +8 -0
- package/element/index.js +21 -0
- package/element/model.d.ts +36 -0
- package/element/model.js +64 -0
- package/element/player.d.ts +196 -0
- package/element/player.js +680 -0
- package/element/practice.d.ts +75 -0
- package/element/practice.js +150 -0
- package/element/scramble.d.ts +50 -0
- package/element/scramble.js +107 -0
- package/element/sequence.d.ts +126 -0
- package/element/sequence.js +254 -0
- package/element/styles.d.ts +32 -0
- package/element/styles.js +196 -0
- package/image/cache.d.ts +21 -0
- package/image/cache.js +66 -0
- package/image/index.d.ts +2 -0
- package/image/index.js +2 -0
- package/image/png-node.d.ts +6 -0
- package/image/png-node.js +10 -0
- package/image/svg.d.ts +36 -0
- package/image/svg.js +215 -0
- package/lbl/index.d.ts +10 -0
- package/lbl/index.js +31 -0
- package/methods/index.d.ts +20 -0
- package/methods/index.js +33 -0
- package/package.json +133 -0
- package/petrus/index.d.ts +11 -0
- package/petrus/index.js +27 -0
- package/react/index.d.ts +176 -0
- package/react/index.js +228 -0
- package/render/arrows.d.ts +68 -0
- package/render/arrows.js +190 -0
- package/render/build/attachments.d.ts +35 -0
- package/render/build/attachments.js +94 -0
- package/render/build/models.d.ts +9 -0
- package/render/build/models.js +27 -0
- package/render/build/tiles.d.ts +48 -0
- package/render/build/tiles.js +194 -0
- package/render/gltf.d.ts +10 -0
- package/render/gltf.js +106 -0
- package/render/index.d.ts +12 -0
- package/render/index.js +12 -0
- package/render/layers.d.ts +21 -0
- package/render/layers.js +24 -0
- package/render/pictures.d.ts +46 -0
- package/render/pictures.js +80 -0
- package/render/pieceModels.d.ts +38 -0
- package/render/pieceModels.js +43 -0
- package/render/renderer.d.ts +165 -0
- package/render/renderer.js +647 -0
- package/render/tile.d.ts +114 -0
- package/render/tile.js +390 -0
- package/render/viewports.d.ts +25 -0
- package/render/viewports.js +26 -0
- package/roux/cmll.d.ts +35 -0
- package/roux/cmll.js +121 -0
- package/roux/cmllData.d.ts +12 -0
- package/roux/cmllData.js +49 -0
- package/roux/index.d.ts +52 -0
- package/roux/index.js +175 -0
- package/skin/attachments.d.ts +82 -0
- package/skin/attachments.js +110 -0
- package/skin/index.d.ts +3 -0
- package/skin/index.js +3 -0
- package/skin/shapes.d.ts +112 -0
- package/skin/shapes.js +147 -0
- package/skin/skin.d.ts +911 -0
- package/skin/skin.js +412 -0
- package/solve/client.d.ts +58 -0
- package/solve/client.js +56 -0
- package/solve/handle.d.ts +6 -0
- package/solve/handle.js +38 -0
- package/solve/index.d.ts +8 -0
- package/solve/index.js +7 -0
- package/solve/lse.d.ts +42 -0
- package/solve/lse.js +151 -0
- package/solve/pieces.d.ts +18 -0
- package/solve/pieces.js +24 -0
- package/solve/protocol.d.ts +70 -0
- package/solve/protocol.js +16 -0
- package/solve/scramble.d.ts +76 -0
- package/solve/scramble.js +187 -0
- package/solve/stage.d.ts +108 -0
- package/solve/stage.js +532 -0
- package/solve/twophase.d.ts +36 -0
- package/solve/twophase.js +229 -0
- package/solve/worker.d.ts +6 -0
- package/solve/worker.js +15 -0
- package/timeline/codec.d.ts +23 -0
- package/timeline/codec.js +84 -0
- package/timeline/index.d.ts +7 -0
- package/timeline/index.js +7 -0
- package/timeline/recording.d.ts +19 -0
- package/timeline/recording.js +20 -0
- package/timeline/replay.d.ts +68 -0
- package/timeline/replay.js +145 -0
- package/timeline/segments.d.ts +31 -0
- package/timeline/segments.js +39 -0
- package/timeline/share.d.ts +30 -0
- package/timeline/share.js +62 -0
- package/timeline/stats.d.ts +34 -0
- package/timeline/stats.js +56 -0
- package/timeline/timings.d.ts +30 -0
- package/timeline/timings.js +54 -0
- package/zz/index.d.ts +29 -0
- package/zz/index.js +68 -0
- package/zz/seeds.d.ts +9 -0
- package/zz/seeds.js +29 -0
package/core/arrows.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn arrows — what an arrow over a 3D cube needs to show a move: which
|
|
3
|
+
* layers turn, about which axis, which way and how far, in the CUBE'S OWN
|
|
4
|
+
* coordinates (its centres). A renderer draws one arrow per layer, so a wide
|
|
5
|
+
* r gets two, a rotation three.
|
|
6
|
+
*
|
|
7
|
+
* `frame` is how the cube is held (OrientationTracker / SequenceTracker):
|
|
8
|
+
* after an x in the algorithm, the U you're asked to turn is the physical F,
|
|
9
|
+
* and its arrow belongs there.
|
|
10
|
+
*/
|
|
11
|
+
import { type Frame } from "./frames.js";
|
|
12
|
+
import { type Axis } from "./geometry.js";
|
|
13
|
+
import { type Move } from "./moves.js";
|
|
14
|
+
export interface TurnArrow {
|
|
15
|
+
axis: Axis;
|
|
16
|
+
/** Layer coordinates along the axis (-1, 0, 1) that turn — one arrow each. */
|
|
17
|
+
layers: number[];
|
|
18
|
+
/**
|
|
19
|
+
* Counter-clockwise quarter turns about +axis, the way it's written: ±1 a
|
|
20
|
+
* single turn, ±2 a double (R2' = the other way than R2), ±3 a triple.
|
|
21
|
+
*/
|
|
22
|
+
quarters: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function turnArrow(move: Move, frame?: Frame): TurnArrow;
|
package/core/arrows.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn arrows — what an arrow over a 3D cube needs to show a move: which
|
|
3
|
+
* layers turn, about which axis, which way and how far, in the CUBE'S OWN
|
|
4
|
+
* coordinates (its centres). A renderer draws one arrow per layer, so a wide
|
|
5
|
+
* r gets two, a rotation three.
|
|
6
|
+
*
|
|
7
|
+
* `frame` is how the cube is held (OrientationTracker / SequenceTracker):
|
|
8
|
+
* after an x in the algorithm, the U you're asked to turn is the physical F,
|
|
9
|
+
* and its arrow belongs there.
|
|
10
|
+
*/
|
|
11
|
+
import { IDENTITY_FRAME } from "./frames.js";
|
|
12
|
+
import { apply } from "./geometry.js";
|
|
13
|
+
import { FAMILY, writtenQuarters } from "./moves.js";
|
|
14
|
+
export function turnArrow(move, frame = IDENTITY_FRAME) {
|
|
15
|
+
const def = FAMILY[move.family];
|
|
16
|
+
const unit = [0, 0, 0];
|
|
17
|
+
unit[def.axis] = 1;
|
|
18
|
+
const v = apply(frame.matrix, unit);
|
|
19
|
+
const axis = v.findIndex((c) => c !== 0);
|
|
20
|
+
const sign = v[axis];
|
|
21
|
+
return { axis, layers: def.layers.map((l) => l * sign), quarters: def.q * writtenQuarters(move) * sign };
|
|
22
|
+
}
|
package/core/bytes.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Byte helpers for compact, URL-safe encodings (state codec here, recordings
|
|
3
|
+
* and share links in @cubecore/timeline): unsigned LEB128 varints, fixed-width
|
|
4
|
+
* bit packing, base64url without padding. Readers are strict: running out of
|
|
5
|
+
* bytes returns null instead of throwing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function writeVarint(out: number[], value: number): void;
|
|
8
|
+
/** Append `values`, each `width` bits (≤ 24), least significant first. */
|
|
9
|
+
export declare function packBits(out: number[], values: readonly number[], width: number): void;
|
|
10
|
+
/** Append fields of different widths (each ≤ 24 bits) as one bit stream. */
|
|
11
|
+
export declare function packFields(out: number[], values: readonly number[], widths: readonly number[]): void;
|
|
12
|
+
export declare function writeText(out: number[], text: string): void;
|
|
13
|
+
export declare class ByteReader {
|
|
14
|
+
private readonly b;
|
|
15
|
+
pos: number;
|
|
16
|
+
constructor(b: Uint8Array);
|
|
17
|
+
byte(): number | null;
|
|
18
|
+
varint(): number | null;
|
|
19
|
+
packed(count: number, width: number): number[] | null;
|
|
20
|
+
/** Read fields written by `packFields`. */
|
|
21
|
+
fields(widths: readonly number[]): number[] | null;
|
|
22
|
+
bytes(count: number): Uint8Array | null;
|
|
23
|
+
text(maxBytes?: number): string | null;
|
|
24
|
+
get done(): boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare const toBase64Url: (bytes: readonly number[] | Uint8Array) => string;
|
|
27
|
+
export declare function fromBase64Url(text: string): Uint8Array | null;
|
package/core/bytes.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Byte helpers for compact, URL-safe encodings (state codec here, recordings
|
|
3
|
+
* and share links in @cubecore/timeline): unsigned LEB128 varints, fixed-width
|
|
4
|
+
* bit packing, base64url without padding. Readers are strict: running out of
|
|
5
|
+
* bytes returns null instead of throwing.
|
|
6
|
+
*/
|
|
7
|
+
export function writeVarint(out, value) {
|
|
8
|
+
let n = Math.max(0, Math.floor(value));
|
|
9
|
+
while (n >= 128) {
|
|
10
|
+
out.push((n % 128) | 128);
|
|
11
|
+
n = Math.floor(n / 128);
|
|
12
|
+
}
|
|
13
|
+
out.push(n);
|
|
14
|
+
}
|
|
15
|
+
/** Append `values`, each `width` bits (≤ 24), least significant first. */
|
|
16
|
+
export function packBits(out, values, width) {
|
|
17
|
+
let acc = 0, bits = 0;
|
|
18
|
+
for (const v of values) {
|
|
19
|
+
acc |= v << bits;
|
|
20
|
+
bits += width;
|
|
21
|
+
while (bits >= 8) {
|
|
22
|
+
out.push(acc & 255);
|
|
23
|
+
acc >>>= 8;
|
|
24
|
+
bits -= 8;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (bits > 0)
|
|
28
|
+
out.push(acc & 255);
|
|
29
|
+
}
|
|
30
|
+
/** Append fields of different widths (each ≤ 24 bits) as one bit stream. */
|
|
31
|
+
export function packFields(out, values, widths) {
|
|
32
|
+
let acc = 0, bits = 0;
|
|
33
|
+
values.forEach((v, i) => {
|
|
34
|
+
acc |= v << bits;
|
|
35
|
+
bits += widths[i];
|
|
36
|
+
while (bits >= 8) {
|
|
37
|
+
out.push(acc & 255);
|
|
38
|
+
acc >>>= 8;
|
|
39
|
+
bits -= 8;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (bits > 0)
|
|
43
|
+
out.push(acc & 255);
|
|
44
|
+
}
|
|
45
|
+
export function writeText(out, text) {
|
|
46
|
+
const bytes = new TextEncoder().encode(text);
|
|
47
|
+
writeVarint(out, bytes.length);
|
|
48
|
+
out.push(...bytes);
|
|
49
|
+
}
|
|
50
|
+
export class ByteReader {
|
|
51
|
+
b;
|
|
52
|
+
pos = 0;
|
|
53
|
+
constructor(b) {
|
|
54
|
+
this.b = b;
|
|
55
|
+
}
|
|
56
|
+
byte() {
|
|
57
|
+
return this.pos < this.b.length ? this.b[this.pos++] : null;
|
|
58
|
+
}
|
|
59
|
+
varint() {
|
|
60
|
+
let r = 0, scale = 1;
|
|
61
|
+
for (let i = 0; i < 7; i++) {
|
|
62
|
+
const x = this.byte();
|
|
63
|
+
if (x === null)
|
|
64
|
+
return null;
|
|
65
|
+
r += (x & 127) * scale;
|
|
66
|
+
if (x < 128)
|
|
67
|
+
return r;
|
|
68
|
+
scale *= 128;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
packed(count, width) {
|
|
73
|
+
const need = Math.ceil((count * width) / 8);
|
|
74
|
+
if (this.pos + need > this.b.length)
|
|
75
|
+
return null;
|
|
76
|
+
const out = [];
|
|
77
|
+
let acc = 0, bits = 0, p = this.pos;
|
|
78
|
+
for (let i = 0; i < count; i++) {
|
|
79
|
+
while (bits < width) {
|
|
80
|
+
acc |= this.b[p++] << bits;
|
|
81
|
+
bits += 8;
|
|
82
|
+
}
|
|
83
|
+
out.push(acc & ((1 << width) - 1));
|
|
84
|
+
acc >>>= width;
|
|
85
|
+
bits -= width;
|
|
86
|
+
}
|
|
87
|
+
this.pos += need;
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
/** Read fields written by `packFields`. */
|
|
91
|
+
fields(widths) {
|
|
92
|
+
const total = widths.reduce((a, b) => a + b, 0);
|
|
93
|
+
const need = Math.ceil(total / 8);
|
|
94
|
+
if (this.pos + need > this.b.length)
|
|
95
|
+
return null;
|
|
96
|
+
const out = [];
|
|
97
|
+
let acc = 0, bits = 0, p = this.pos;
|
|
98
|
+
for (const w of widths) {
|
|
99
|
+
while (bits < w) {
|
|
100
|
+
acc |= this.b[p++] << bits;
|
|
101
|
+
bits += 8;
|
|
102
|
+
}
|
|
103
|
+
out.push(acc & ((1 << w) - 1));
|
|
104
|
+
acc >>>= w;
|
|
105
|
+
bits -= w;
|
|
106
|
+
}
|
|
107
|
+
this.pos += need;
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
bytes(count) {
|
|
111
|
+
if (this.pos + count > this.b.length)
|
|
112
|
+
return null;
|
|
113
|
+
const out = this.b.slice(this.pos, this.pos + count);
|
|
114
|
+
this.pos += count;
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
text(maxBytes = 256) {
|
|
118
|
+
const n = this.varint();
|
|
119
|
+
if (n === null || n > maxBytes)
|
|
120
|
+
return null;
|
|
121
|
+
const raw = this.bytes(n);
|
|
122
|
+
if (!raw)
|
|
123
|
+
return null;
|
|
124
|
+
try {
|
|
125
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(raw);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
get done() {
|
|
132
|
+
return this.pos === this.b.length;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export const toBase64Url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
136
|
+
export function fromBase64Url(text) {
|
|
137
|
+
if (!/^[A-Za-z0-9_-]*$/.test(text) || text.length % 4 === 1)
|
|
138
|
+
return null;
|
|
139
|
+
try {
|
|
140
|
+
return Uint8Array.from(atob(text.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centre spin — the one thing a facelet permutation can't express. After U the
|
|
3
|
+
* U centre is still at facelet 4, but it has turned a quarter; a logo or an
|
|
4
|
+
* arrow drawn on it has to turn too. Supercube-style puzzles (and pictures
|
|
5
|
+
* with a brand logo) need it; solving never does.
|
|
6
|
+
*
|
|
7
|
+
* `CenterSpins[c]` belongs to the centre whose home face is `c` (U R F D L B)
|
|
8
|
+
* and counts counter-clockwise quarter turns (seen from outside) of that
|
|
9
|
+
* centre relative to `FACE_BASIS` of the face it currently sits on: spin 0 =
|
|
10
|
+
* the sticker's "up" points along that face's `b` axis.
|
|
11
|
+
*/
|
|
12
|
+
import { type Face, type Vec3 } from "./geometry.js";
|
|
13
|
+
import { type Move } from "./moves.js";
|
|
14
|
+
import { type State } from "./state.js";
|
|
15
|
+
export type CenterSpins = Uint8Array;
|
|
16
|
+
export declare const solvedSpins: () => CenterSpins;
|
|
17
|
+
/** Direction the centre's "up" points when it sits on `face` with spin `spin`. */
|
|
18
|
+
export declare function centerUp(face: Face, spin: number): Vec3;
|
|
19
|
+
/** Spins after `move` is applied to `state` (the state BEFORE the move). */
|
|
20
|
+
export declare function advanceSpins(spins: CenterSpins, state: State, move: Move): CenterSpins;
|
|
21
|
+
/** Spins after a sequence of moves from `start` (whose spins default to 0). */
|
|
22
|
+
export declare function spinsAfter(start: State, moves: readonly Move[] | string, spins?: CenterSpins): CenterSpins;
|
|
23
|
+
/**
|
|
24
|
+
* How far sticker `sticker` (a home facelet index) is turned in its face's
|
|
25
|
+
* plane — counter-clockwise quarter turns relative to `FACE_BASIS` of the
|
|
26
|
+
* face it currently sits on, compared with how it sat at home. Anything drawn
|
|
27
|
+
* on a sticker (a logo, a decal, holes) turns by this much.
|
|
28
|
+
*
|
|
29
|
+
* Centres: their spin (`spins`, see above). Edges and corners: read off the
|
|
30
|
+
* piece itself — the direction towards one of its other stickers.
|
|
31
|
+
*/
|
|
32
|
+
export declare function stickerTurn(state: State, sticker: number, spins?: CenterSpins): number;
|
package/core/centers.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centre spin — the one thing a facelet permutation can't express. After U the
|
|
3
|
+
* U centre is still at facelet 4, but it has turned a quarter; a logo or an
|
|
4
|
+
* arrow drawn on it has to turn too. Supercube-style puzzles (and pictures
|
|
5
|
+
* with a brand logo) need it; solving never does.
|
|
6
|
+
*
|
|
7
|
+
* `CenterSpins[c]` belongs to the centre whose home face is `c` (U R F D L B)
|
|
8
|
+
* and counts counter-clockwise quarter turns (seen from outside) of that
|
|
9
|
+
* centre relative to `FACE_BASIS` of the face it currently sits on: spin 0 =
|
|
10
|
+
* the sticker's "up" points along that face's `b` axis.
|
|
11
|
+
*/
|
|
12
|
+
import { CUBIE_OF_FACELET, FACE_BASIS, FACELETS, faceOfNormal, rotate } from "./geometry.js";
|
|
13
|
+
import { FAMILY } from "./moves.js";
|
|
14
|
+
import { parseAlg } from "./notation.js";
|
|
15
|
+
import { applyMove } from "./state.js";
|
|
16
|
+
export const solvedSpins = () => new Uint8Array(6);
|
|
17
|
+
const eq = (p, q) => p[0] === q[0] && p[1] === q[1] && p[2] === q[2];
|
|
18
|
+
/** `v` turned `q` CCW quarters about the outward normal of `face`. */
|
|
19
|
+
function aboutNormal(v, face, q) {
|
|
20
|
+
const n = FACE_BASIS[face].n;
|
|
21
|
+
const axis = n[0] !== 0 ? 0 : n[1] !== 0 ? 1 : 2;
|
|
22
|
+
return rotate(v, axis, q * n[axis]);
|
|
23
|
+
}
|
|
24
|
+
/** Direction the centre's "up" points when it sits on `face` with spin `spin`. */
|
|
25
|
+
export function centerUp(face, spin) {
|
|
26
|
+
return aboutNormal(FACE_BASIS[face].b, face, spin);
|
|
27
|
+
}
|
|
28
|
+
/** Spins after `move` is applied to `state` (the state BEFORE the move). */
|
|
29
|
+
export function advanceSpins(spins, state, move) {
|
|
30
|
+
const def = FAMILY[move.family];
|
|
31
|
+
const quarters = def.q * (move.amount === -1 ? -1 : move.amount === 2 ? 2 : 1);
|
|
32
|
+
const out = new Uint8Array(spins);
|
|
33
|
+
for (let c = 0; c < 6; c++) {
|
|
34
|
+
const f = FACELETS[state.indexOf(c * 9 + 4)];
|
|
35
|
+
if (!def.layers.includes(f.pos[def.axis]))
|
|
36
|
+
continue;
|
|
37
|
+
const up = rotate(centerUp(f.face, spins[c]), def.axis, quarters);
|
|
38
|
+
const face = faceOfNormal(rotate(f.normal, def.axis, quarters));
|
|
39
|
+
for (let k = 0; k < 4; k++)
|
|
40
|
+
if (eq(centerUp(face, k), up))
|
|
41
|
+
out[c] = k;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** Spins after a sequence of moves from `start` (whose spins default to 0). */
|
|
46
|
+
export function spinsAfter(start, moves, spins = solvedSpins()) {
|
|
47
|
+
let s = start;
|
|
48
|
+
let out = spins;
|
|
49
|
+
for (const m of typeof moves === "string" ? parseAlg(moves) : moves) {
|
|
50
|
+
out = advanceSpins(out, s, m);
|
|
51
|
+
s = applyMove(s, m);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* How far sticker `sticker` (a home facelet index) is turned in its face's
|
|
57
|
+
* plane — counter-clockwise quarter turns relative to `FACE_BASIS` of the
|
|
58
|
+
* face it currently sits on, compared with how it sat at home. Anything drawn
|
|
59
|
+
* on a sticker (a logo, a decal, holes) turns by this much.
|
|
60
|
+
*
|
|
61
|
+
* Centres: their spin (`spins`, see above). Edges and corners: read off the
|
|
62
|
+
* piece itself — the direction towards one of its other stickers.
|
|
63
|
+
*/
|
|
64
|
+
export function stickerTurn(state, sticker, spins = solvedSpins()) {
|
|
65
|
+
if (sticker % 9 === 4)
|
|
66
|
+
return spins[Math.floor(sticker / 9)];
|
|
67
|
+
const home = FACELETS[sticker];
|
|
68
|
+
const partner = CUBIE_OF_FACELET[sticker].facelets.find((f) => f !== sticker);
|
|
69
|
+
const now = FACELETS[state.indexOf(sticker)];
|
|
70
|
+
const partnerNow = FACELETS[state.indexOf(partner)];
|
|
71
|
+
const inPlane = (face, dir) => {
|
|
72
|
+
const { a, b } = FACE_BASIS[face];
|
|
73
|
+
return [dir[0] * a[0] + dir[1] * a[1] + dir[2] * a[2], dir[0] * b[0] + dir[1] * b[1] + dir[2] * b[2]];
|
|
74
|
+
};
|
|
75
|
+
let d = inPlane(home.face, FACELETS[partner].normal);
|
|
76
|
+
const target = inPlane(now.face, partnerNow.normal);
|
|
77
|
+
for (let k = 0; k < 4; k++) {
|
|
78
|
+
if (d[0] === target[0] && d[1] === target[1])
|
|
79
|
+
return k;
|
|
80
|
+
d = [-d[1], d[0]];
|
|
81
|
+
}
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
package/core/checks.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check primitives, written for the CANONICAL frame (run them on
|
|
3
|
+
* `view(state, frame)`): first layer on D, last layer on U. Method packages
|
|
4
|
+
* (@cubecore/cfop, roux, zz, petrus, lbl) build their stages from these, so
|
|
5
|
+
* every method is colour neutral by construction.
|
|
6
|
+
*
|
|
7
|
+
* "Solved" pieces show the colour of the centre of the face they sit on. No
|
|
8
|
+
* absolute colours anywhere — methods that move centres (Roux M slices)
|
|
9
|
+
* compare pieces to each other with the helpers below instead.
|
|
10
|
+
*/
|
|
11
|
+
import { type Cubie, type Face, type Vec3 } from "./geometry.js";
|
|
12
|
+
import { type State, isSolved } from "./state.js";
|
|
13
|
+
/** Facelet index of a face's centre. */
|
|
14
|
+
export declare const centerIdx: (face: Face) => number;
|
|
15
|
+
/** Colour class of the opposite face (U↔D, R↔L, F↔B). */
|
|
16
|
+
export declare const opposite: (color: number) => number;
|
|
17
|
+
export declare function cubieAt(x: number, y: number, z: number): Cubie;
|
|
18
|
+
export declare const cubiesWhere: (pred: (p: Vec3) => boolean) => Cubie[];
|
|
19
|
+
/** Facelet of `cubie` that faces `face`, or undefined. */
|
|
20
|
+
export declare function faceletOn(cubie: Cubie, face: Face): number | undefined;
|
|
21
|
+
export declare const matchesCenter: (s: State, facelet: number) => boolean;
|
|
22
|
+
export declare const cubieSolved: (s: State, c: Cubie) => boolean;
|
|
23
|
+
export declare const allSolved: (s: State, cubies: readonly Cubie[]) => boolean;
|
|
24
|
+
export declare function upToAuf(s: State, pred: (s: State) => boolean): boolean;
|
|
25
|
+
export type Slot = "FR" | "FL" | "BR" | "BL";
|
|
26
|
+
export declare const SLOTS: readonly Slot[];
|
|
27
|
+
export declare const U_CORNERS: Cubie[];
|
|
28
|
+
export declare const U_EDGES: Cubie[];
|
|
29
|
+
export declare const crossSolved: (s: State) => boolean;
|
|
30
|
+
export declare const firstLayerSolved: (s: State) => boolean;
|
|
31
|
+
export declare const f2lSolved: (s: State) => boolean;
|
|
32
|
+
export declare const pairSolved: (s: State, slot: Slot) => boolean;
|
|
33
|
+
export declare const slotCornerSolved: (s: State, slot: Slot) => boolean;
|
|
34
|
+
export declare const slotEdgeSolved: (s: State, slot: Slot) => boolean;
|
|
35
|
+
export declare const solvedPairs: (s: State) => Slot[];
|
|
36
|
+
export declare const topColor: (s: State) => number;
|
|
37
|
+
/** OLL: the whole top face shows the top centre's colour. */
|
|
38
|
+
export declare const topOriented: (s: State) => boolean;
|
|
39
|
+
export declare const topCornersOriented: (s: State) => boolean;
|
|
40
|
+
export declare const topEdgesOriented: (s: State) => boolean;
|
|
41
|
+
/** PLL done, possibly short of the final U turn (AUF). */
|
|
42
|
+
export declare const solvedUpToAuf: (s: State) => boolean;
|
|
43
|
+
/** Last-layer corners in place (relative to each other and F2L), up to AUF. */
|
|
44
|
+
export declare const llCornersPermuted: (s: State) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* EO for the F/B axis (ZZ, Petrus): an edge is good if it can be solved
|
|
47
|
+
* without F/B quarter turns. Look at its sticker on U/D (or, for an E-slice
|
|
48
|
+
* edge, on F/B): U/D colour → good, L/R colour → bad, F/B colour → bad only
|
|
49
|
+
* if the other sticker is a U/D colour.
|
|
50
|
+
*/
|
|
51
|
+
export declare function edgeOrientedFB(s: State, edge: Cubie): boolean;
|
|
52
|
+
export declare const edgesOrientedFB: (s: State) => boolean;
|
|
53
|
+
export { isSolved };
|
package/core/checks.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check primitives, written for the CANONICAL frame (run them on
|
|
3
|
+
* `view(state, frame)`): first layer on D, last layer on U. Method packages
|
|
4
|
+
* (@cubecore/cfop, roux, zz, petrus, lbl) build their stages from these, so
|
|
5
|
+
* every method is colour neutral by construction.
|
|
6
|
+
*
|
|
7
|
+
* "Solved" pieces show the colour of the centre of the face they sit on. No
|
|
8
|
+
* absolute colours anywhere — methods that move centres (Roux M slices)
|
|
9
|
+
* compare pieces to each other with the helpers below instead.
|
|
10
|
+
*/
|
|
11
|
+
import { CUBIES, FACELETS, FACES } from "./geometry.js";
|
|
12
|
+
import { parseAlg } from "./notation.js";
|
|
13
|
+
import { applyMoves, colorAt, isSolved } from "./state.js";
|
|
14
|
+
// ─── helpers ───
|
|
15
|
+
const faceIndex = (face) => FACES.indexOf(face);
|
|
16
|
+
/** Facelet index of a face's centre. */
|
|
17
|
+
export const centerIdx = (face) => faceIndex(face) * 9 + 4;
|
|
18
|
+
/** Colour class of the opposite face (U↔D, R↔L, F↔B). */
|
|
19
|
+
export const opposite = (color) => (color + 3) % 6;
|
|
20
|
+
export function cubieAt(x, y, z) {
|
|
21
|
+
return CUBIES.find((c) => c.pos[0] === x && c.pos[1] === y && c.pos[2] === z);
|
|
22
|
+
}
|
|
23
|
+
export const cubiesWhere = (pred) => CUBIES.filter((c) => pred(c.pos));
|
|
24
|
+
/** Facelet of `cubie` that faces `face`, or undefined. */
|
|
25
|
+
export function faceletOn(cubie, face) {
|
|
26
|
+
return cubie.facelets.find((f) => FACELETS[f].face === face);
|
|
27
|
+
}
|
|
28
|
+
export const matchesCenter = (s, facelet) => colorAt(s, facelet) === colorAt(s, centerIdx(FACELETS[facelet].face));
|
|
29
|
+
export const cubieSolved = (s, c) => c.facelets.every((f) => matchesCenter(s, f));
|
|
30
|
+
export const allSolved = (s, cubies) => cubies.every((c) => cubieSolved(s, c));
|
|
31
|
+
/** True if some U-layer turn (none, U, U2, U') makes `pred` hold. */
|
|
32
|
+
const U_TURNS = [[], parseAlg("U"), parseAlg("U2"), parseAlg("U'")];
|
|
33
|
+
export function upToAuf(s, pred) {
|
|
34
|
+
return U_TURNS.some((t) => pred(t.length ? applyMoves(s, t) : s));
|
|
35
|
+
}
|
|
36
|
+
export const SLOTS = ["FR", "FL", "BR", "BL"];
|
|
37
|
+
const SLOT_XZ = { FR: [1, 1], FL: [-1, 1], BR: [1, -1], BL: [-1, -1] };
|
|
38
|
+
const slotCorner = (slot) => cubieAt(SLOT_XZ[slot][0], -1, SLOT_XZ[slot][1]);
|
|
39
|
+
const slotEdge = (slot) => cubieAt(SLOT_XZ[slot][0], 0, SLOT_XZ[slot][1]);
|
|
40
|
+
// ─── layers (first layer on D) ───
|
|
41
|
+
const CROSS = cubiesWhere((p) => p[1] === -1 && p.filter((v) => v !== 0).length === 2);
|
|
42
|
+
const FIRST_LAYER = cubiesWhere((p) => p[1] === -1);
|
|
43
|
+
const F2L = cubiesWhere((p) => p[1] <= 0);
|
|
44
|
+
export const U_CORNERS = cubiesWhere((p) => p[1] === 1 && p[0] !== 0 && p[2] !== 0);
|
|
45
|
+
export const U_EDGES = cubiesWhere((p) => p[1] === 1 && (p[0] === 0) !== (p[2] === 0));
|
|
46
|
+
export const crossSolved = (s) => allSolved(s, CROSS);
|
|
47
|
+
export const firstLayerSolved = (s) => allSolved(s, FIRST_LAYER);
|
|
48
|
+
export const f2lSolved = (s) => allSolved(s, F2L);
|
|
49
|
+
export const pairSolved = (s, slot) => cubieSolved(s, slotCorner(slot)) && cubieSolved(s, slotEdge(slot));
|
|
50
|
+
export const slotCornerSolved = (s, slot) => cubieSolved(s, slotCorner(slot));
|
|
51
|
+
export const slotEdgeSolved = (s, slot) => cubieSolved(s, slotEdge(slot));
|
|
52
|
+
export const solvedPairs = (s) => SLOTS.filter((slot) => pairSolved(s, slot));
|
|
53
|
+
export const topColor = (s) => colorAt(s, centerIdx("U"));
|
|
54
|
+
const onTop = (s, cubies) => cubies.every((c) => colorAt(s, faceletOn(c, "U")) === topColor(s));
|
|
55
|
+
/** OLL: the whole top face shows the top centre's colour. */
|
|
56
|
+
export const topOriented = (s) => onTop(s, U_CORNERS) && onTop(s, U_EDGES);
|
|
57
|
+
export const topCornersOriented = (s) => onTop(s, U_CORNERS);
|
|
58
|
+
export const topEdgesOriented = (s) => onTop(s, U_EDGES);
|
|
59
|
+
/** PLL done, possibly short of the final U turn (AUF). */
|
|
60
|
+
export const solvedUpToAuf = (s) => upToAuf(s, isSolved);
|
|
61
|
+
/** Last-layer corners in place (relative to each other and F2L), up to AUF. */
|
|
62
|
+
export const llCornersPermuted = (s) => upToAuf(s, (t) => allSolved(t, U_CORNERS));
|
|
63
|
+
// ─── Edge orientation ───
|
|
64
|
+
/**
|
|
65
|
+
* EO for the F/B axis (ZZ, Petrus): an edge is good if it can be solved
|
|
66
|
+
* without F/B quarter turns. Look at its sticker on U/D (or, for an E-slice
|
|
67
|
+
* edge, on F/B): U/D colour → good, L/R colour → bad, F/B colour → bad only
|
|
68
|
+
* if the other sticker is a U/D colour.
|
|
69
|
+
*/
|
|
70
|
+
export function edgeOrientedFB(s, edge) {
|
|
71
|
+
const ud = [colorAt(s, centerIdx("U")), colorAt(s, centerIdx("D"))];
|
|
72
|
+
const lr = [colorAt(s, centerIdx("L")), colorAt(s, centerIdx("R"))];
|
|
73
|
+
const primaryFaces = edge.pos[1] !== 0 ? ["U", "D"] : ["F", "B"];
|
|
74
|
+
const primary = edge.facelets.find((f) => primaryFaces.includes(FACELETS[f].face));
|
|
75
|
+
const other = edge.facelets.find((f) => f !== primary);
|
|
76
|
+
const c = colorAt(s, primary);
|
|
77
|
+
if (ud.includes(c))
|
|
78
|
+
return true;
|
|
79
|
+
if (lr.includes(c))
|
|
80
|
+
return false;
|
|
81
|
+
return !ud.includes(colorAt(s, other));
|
|
82
|
+
}
|
|
83
|
+
const EDGES = CUBIES.filter((c) => c.kind === "edge");
|
|
84
|
+
export const edgesOrientedFB = (s) => EDGES.every((e) => edgeOrientedFB(s, e));
|
|
85
|
+
export { isSolved };
|
package/core/cubies.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cube as pieces: corner / edge permutation and orientation in the
|
|
3
|
+
* standard (Kociemba) numbering, plus how the centres are turned (a Frame).
|
|
4
|
+
* The compact state codec and the two-phase solver work on this; everything
|
|
5
|
+
* else in cubecore uses the 54-sticker State.
|
|
6
|
+
*
|
|
7
|
+
* Corners URF UFL ULB UBR DFR DLF DBL DRB, edges UR UF UL UB DR DF DL DB FR FL
|
|
8
|
+
* BL BR. Corner twist counts how far the U/D sticker is turned from the U/D
|
|
9
|
+
* position (0..2, clockwise); an edge is flipped (1) when its first sticker —
|
|
10
|
+
* the U/D one, or F/B for the middle-layer edges — isn't in the first place.
|
|
11
|
+
*/
|
|
12
|
+
import { type Frame } from "./frames.js";
|
|
13
|
+
import type { State } from "./state.js";
|
|
14
|
+
/** Facelets of each corner position, U/D sticker first, then clockwise. */
|
|
15
|
+
export declare const CORNER_FACELETS: readonly (readonly [number, number, number])[];
|
|
16
|
+
/** Facelets of each edge position, the reference sticker first. */
|
|
17
|
+
export declare const EDGE_FACELETS: readonly (readonly [number, number])[];
|
|
18
|
+
export interface CubieState {
|
|
19
|
+
/** How the centres are turned (FRAMES id 0 = at home). */
|
|
20
|
+
frame: Frame;
|
|
21
|
+
cp: number[];
|
|
22
|
+
co: number[];
|
|
23
|
+
ep: number[];
|
|
24
|
+
eo: number[];
|
|
25
|
+
}
|
|
26
|
+
/** Pieces of a state, or null if it isn't made of real pieces. */
|
|
27
|
+
export declare function toCubies(state: State): CubieState | null;
|
|
28
|
+
export declare function fromCubies(c: CubieState): State;
|
|
29
|
+
/** Parity of a permutation (0 even, 1 odd). */
|
|
30
|
+
export declare function parity(p: readonly number[]): number;
|
|
31
|
+
/** Reachable by turning (not a reassembled cube): twist sum ≡ 0 mod 3, flips even, equal parities. */
|
|
32
|
+
export declare function isSolvable(c: CubieState): boolean;
|
|
33
|
+
/** Lehmer rank of a permutation of 0..n-1: 0 .. n!-1. */
|
|
34
|
+
export declare function permRank(p: readonly number[]): number;
|
|
35
|
+
export declare function permUnrank(rank: number, n: number): number[];
|
|
36
|
+
/**
|
|
37
|
+
* A state from a 54-letter facelet string (Kociemba order U1…U9 R1… F1… D1…
|
|
38
|
+
* L1… B1…, each letter the face whose colour is there) — what smart cubes
|
|
39
|
+
* report and many tools exchange. Colours are read against the centres, so
|
|
40
|
+
* any letters work as long as each centre has its own. Null if the string
|
|
41
|
+
* isn't a reachable cube.
|
|
42
|
+
*/
|
|
43
|
+
export declare function stateFromFacelets(text: string): State | null;
|
|
44
|
+
/** The 54-letter facelet string of a state (each letter = the colour class's home face). */
|
|
45
|
+
export declare function faceletsOf(state: State): string;
|