@wodzik/cubecore 0.1.6 → 0.1.7

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.
@@ -60,9 +60,19 @@ export declare function heldMove(move: string, grip: Grip): string;
60
60
  * y2, y y' = nothing), and slices / wide moves read from face moves with
61
61
  * the core's rotation.
62
62
  */
63
- export declare function heldTokens(moves: readonly TimedMove[], startRotation: string, rotations: readonly RotationRecord[]): HeldToken[];
63
+ export interface HeldOptions {
64
+ /**
65
+ * Wide moves to read from one face turn and the core's rotation — the rest
66
+ * stay a face turn and a rotation. Default r, l, f, b: a u or d is almost
67
+ * always a U / D turn and a y regrip. Slices (both faces of an axis) are
68
+ * always read.
69
+ */
70
+ wide?: readonly string[];
71
+ }
72
+ export declare const DEFAULT_WIDE: readonly string[];
73
+ export declare function heldTokens(moves: readonly TimedMove[], startRotation: string, rotations: readonly RotationRecord[], options?: HeldOptions): HeldToken[];
64
74
  /** A rotation and the face moves around it that make a slice / wide move → that move. */
65
- export declare function mergeSlicesAndWides(tokens: readonly HeldToken[]): HeldToken[];
75
+ export declare function mergeSlicesAndWides(tokens: readonly HeldToken[], allowed?: ReadonlySet<string>): HeldToken[];
66
76
  export interface GripEvent {
67
77
  /** Moves the session had reported before it (its stream position). */
68
78
  after: number;
@@ -28,16 +28,11 @@ export function heldMove(move, grip) {
28
28
  const canonical = Object.keys(grip.face).find((c) => grip.face[c] === face);
29
29
  return canonical ? canonical + move.slice(1) : move;
30
30
  }
31
- /**
32
- * The moves as the solver saw them: re-lettered for the grip each was made
33
- * in (`startRotation` from white top / green front — U up, F front — then
34
- * the rotations), rotations between them (back-to-back ones combined: y y =
35
- * y2, y y' = nothing), and slices / wide moves read from face moves with
36
- * the core's rotation.
37
- */
38
- export function heldTokens(moves, startRotation, rotations) {
31
+ export const DEFAULT_WIDE = ["r", "l", "f", "b"];
32
+ export function heldTokens(moves, startRotation, rotations, options = {}) {
33
+ const allowed = new Set(options.wide ?? DEFAULT_WIDE);
39
34
  let grip = rotateGrip(IDENTITY_GRIP, startRotation);
40
- const rots = snapToSlices(moves, grip, [...rotations].sort((a, b) => a.after - b.after || a.t - b.t));
35
+ const rots = snapToSlices(moves, grip, [...rotations].sort((a, b) => a.after - b.after || a.t - b.t), allowed);
41
36
  const tokens = [];
42
37
  let r = 0;
43
38
  for (let i = 0; i <= moves.length; i++) {
@@ -58,7 +53,7 @@ export function heldTokens(moves, startRotation, rotations) {
58
53
  if (i < moves.length)
59
54
  tokens.push({ kind: "move", move: heldMove(moves[i].move, grip), t: moves[i].t, index: i });
60
55
  }
61
- return mergeSlicesAndWides(tokens);
56
+ return mergeSlicesAndWides(tokens, allowed);
62
57
  }
63
58
  // ─── slices and wide moves ──────────────────────────────────────────────
64
59
  /** A slice / wide move's face moves and its rotation, reported this far apart (ms), are one move. */
@@ -74,12 +69,13 @@ const AXIS_FACES = { x: ["R", "L"], y: ["U", "D"], z: ["F", "B"] };
74
69
  const SLICE_WIDE = ["M", "E", "S", "r", "l", "u", "d", "f", "b"].flatMap((f) => [f, `${f}'`, `${f}2`]);
75
70
  const effect = (alg) => applyMoves(solvedState(), alg).join();
76
71
  const SLICE_WIDE_EFFECT = new Map();
77
- /** The slice / wide move the group (held letters and a rotation) amounts to, or null. */
78
- function sliceOrWide(group) {
72
+ /** The slice (or an `allowed` wide move) the group (held letters and a rotation) amounts to, or null. */
73
+ function sliceOrWide(group, allowed) {
79
74
  if (SLICE_WIDE_EFFECT.size === 0)
80
75
  for (const m of SLICE_WIDE)
81
76
  SLICE_WIDE_EFFECT.set(effect(m), m);
82
- return SLICE_WIDE_EFFECT.get(effect(group.join(" "))) ?? null;
77
+ const m = SLICE_WIDE_EFFECT.get(effect(group.join(" ")));
78
+ return m && ("MES".includes(m[0]) || allowed.has(m[0])) ? m : null;
83
79
  }
84
80
  /**
85
81
  * The consecutive run (up to MAX_GROUP) of moves on the axis nearest to
@@ -111,7 +107,7 @@ function axisRun(moves, from, dir, onAxis, t) {
111
107
  * slice's signature (both faces of the axis) is trusted: one face and a
112
108
  * rotation could as well be a face turn and a real regrip.
113
109
  */
114
- function snapToSlices(moves, startGrip, rotations) {
110
+ function snapToSlices(moves, startGrip, rotations, allowed) {
115
111
  let grip = startGrip;
116
112
  const out = rotations.map((r) => ({ ...r }));
117
113
  for (const r of out) {
@@ -133,7 +129,7 @@ function snapToSlices(moves, startGrip, rotations) {
133
129
  const far = idx.some((j) => Math.abs(moves[j].t - r.t) > SNAP_MS);
134
130
  if (far && new Set(idx.map((j) => moves[j].move[0])).size < 2)
135
131
  continue;
136
- if (sliceOrWide([...letters(idx), r.move])) {
132
+ if (sliceOrWide([...letters(idx), r.move], allowed)) {
137
133
  r.after = after;
138
134
  // It happened with those moves: its time is theirs.
139
135
  r.t = moves[after > idx[0] ? idx.at(-1) : idx[0]].t;
@@ -146,7 +142,7 @@ function snapToSlices(moves, startGrip, rotations) {
146
142
  return out.sort((a, b) => a.after - b.after || a.t - b.t);
147
143
  }
148
144
  /** A rotation and the face moves around it that make a slice / wide move → that move. */
149
- export function mergeSlicesAndWides(tokens) {
145
+ export function mergeSlicesAndWides(tokens, allowed = new Set(DEFAULT_WIDE)) {
150
146
  const out = [...tokens];
151
147
  for (let k = 0; k < out.length; k++) {
152
148
  const rot = out[k];
@@ -165,7 +161,7 @@ export function mergeSlicesAndWides(tokens) {
165
161
  const groupMoves = group.filter((t) => t.kind === "move");
166
162
  if (groupMoves.length !== group.length - 1 || groupMoves.some((m) => Math.abs(m.t - rot.t) > SAME_MOMENT_MS))
167
163
  continue;
168
- const merged = sliceOrWide(group.map((t) => t.move));
164
+ const merged = sliceOrWide(group.map((t) => t.move), allowed);
169
165
  if (!merged)
170
166
  continue;
171
167
  const first = groupMoves[0];
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.6",
6
+ "version": "0.1.7",
7
7
  "description": "A 3×3×3 cube library for the web, built around smart (Bluetooth) cubes: state, notation, methods, solvers and scrambles, a three.js renderer with skins, pictures, web components, React bindings.",
8
8
  "license": "MPL-2.0",
9
9
  "author": "Tomasz Wodzikowski <wodziszczakow@gmail.com>",