@cubingcenter/scrambler 0.6.3 → 0.8.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/README.md +17 -4
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +10 -2
- package/dist/puzzles/cube2/solver/browser-loader.d.ts +4 -7
- package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/generated-assets.js +8 -3
- package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/search.js +3 -3
- package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
- package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/tables.js +16 -2
- package/dist/puzzles/cube3/index.d.ts +1 -0
- package/dist/puzzles/cube3/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.js +104 -18
- package/dist/puzzles/cube3/scrambler.d.ts +16 -1
- package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube3/scrambler.js +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +48 -22
- package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
- package/dist/puzzles/cube3/solver/index.d.ts +5 -3
- package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/index.js +2 -1
- package/dist/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist/puzzles/cube3/solver/phase1.d.ts +37 -0
- package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase1.js +156 -15
- package/dist/puzzles/cube3/solver/phase2.d.ts +75 -0
- package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase2.js +279 -68
- package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
- package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
- package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
- package/dist/puzzles/cube3/solver/symmetry.js +74 -0
- package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
- package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/tables.js +14 -12
- package/dist/puzzles/cube3/validation.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.js +15 -7
- package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
- package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
- package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
- package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker.js +31 -0
- package/dist/puzzles/cube4/index.d.ts +10 -2
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +6 -2
- package/dist/puzzles/cube4/moves.d.ts +5 -0
- package/dist/puzzles/cube4/moves.d.ts.map +1 -1
- package/dist/puzzles/cube4/moves.js +212 -11
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +29 -1
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/browser-loader.js +227 -32
- package/dist/puzzles/cube4/reduction/center2.d.ts +44 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/center2.js +152 -10
- package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
- package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/edge3.js +23 -3
- package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
- package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/finish3.js +393 -0
- package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
- package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +52 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.js +220 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +15 -1
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/node-loader.js +199 -25
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
- package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +116 -7
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/phase3-search.js +458 -32
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
- package/dist/puzzles/cube4/reduction/solve.d.ts +172 -2
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/solve.js +1201 -180
- package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
- package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
- package/dist/puzzles/cube4/scramble-cache.js +150 -0
- package/dist/puzzles/cube4/scrambler.d.ts +201 -6
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +414 -176
- package/dist/puzzles/cube4/state.d.ts.map +1 -1
- package/dist/puzzles/cube4/state.js +4 -1
- package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
- package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-pool.js +405 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-protocol.js +58 -0
- package/dist/puzzles/cube4/worker.d.ts +26 -0
- package/dist/puzzles/cube4/worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker.js +49 -0
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
- package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/search.js +3 -6
- package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
- package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/tables.js +17 -0
- package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
- package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
- package/dist/puzzles/shared/compressed-fetch.js +226 -31
- package/dist/puzzles/shared/load-once.d.ts +16 -0
- package/dist/puzzles/shared/load-once.d.ts.map +1 -0
- package/dist/puzzles/shared/load-once.js +11 -0
- package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
- package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
- package/dist/puzzles/skewb/solver/search.js +3 -3
- package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
- package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/tables.js +17 -0
- package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
- package/dist-cjs/core/api.js +10 -2
- package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
- package/dist-cjs/puzzles/cube3/moves.js +104 -18
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
- package/dist-cjs/puzzles/cube3/solver/browser-loader.js +48 -22
- package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
- package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
- package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist-cjs/puzzles/cube3/solver/phase1.js +157 -15
- package/dist-cjs/puzzles/cube3/solver/phase2.js +276 -65
- package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
- package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
- package/dist-cjs/puzzles/cube3/validation.js +15 -7
- package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
- package/dist-cjs/puzzles/cube4/index.js +23 -1
- package/dist-cjs/puzzles/cube4/moves.js +213 -11
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +227 -32
- package/dist-cjs/puzzles/cube4/reduction/center2.js +155 -10
- package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
- package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +232 -0
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +200 -25
- package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
- package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +460 -33
- package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
- package/dist-cjs/puzzles/cube4/reduction/solve.js +1203 -172
- package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +416 -174
- package/dist-cjs/puzzles/cube4/state.js +4 -1
- package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
- package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
- package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
- package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
- package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
- package/dist-cjs/puzzles/shared/load-once.js +14 -0
- package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
- package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
- package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
- package/docs/development.md +19 -7
- package/package.json +17 -7
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The package currently ships random-state scramblers for `222` (2x2x2), `333` (3x
|
|
|
16
16
|
- 3x3 quality: random-state target + two-phase solver + verified scramble inversion
|
|
17
17
|
- Pyraminx quality: random-state target + optimal policy-table solver (all 933,120 core states verified)
|
|
18
18
|
- Skewb quality: random-state target + optimal policy-table solver (all 3,149,280 states; exact uniform sampling)
|
|
19
|
-
- 4x4 quality: random-state target (uniform sampler) + reduction solver (4 phases) +
|
|
19
|
+
- 4x4 quality: random-state target (uniform sampler) + reduction solver (4 phases) + exact target reproduction, validated against the sampled state on every generation
|
|
20
20
|
|
|
21
21
|
This library is intended for practice, training, and internal CubingCenter use. Do not present it as an official WCA competition scrambler; sanctioned competitions must use the official program required by the delegate/organization.
|
|
22
22
|
|
|
@@ -59,7 +59,7 @@ console.log(batch.map((item) => item.scramble));
|
|
|
59
59
|
|
|
60
60
|
`generateScramble("333")` returns a legal random-state 3x3 scramble. Its metadata includes the target cubie state, timer-ready facelets, a 54-character facelet string, and solver diagnostics.
|
|
61
61
|
|
|
62
|
-
`generateScramble("pymx")` returns a legal random-state Pyraminx scramble. Its metadata includes the target state, facelets, solution moves and depth. Pyraminx/2x2/3x3 accept `{ skipValidation: true }` to skip the per-call verification for maximum throughput (
|
|
62
|
+
`generateScramble("pymx")` returns a legal random-state Pyraminx scramble. Its metadata includes the target state, facelets, solution moves and depth. Pyraminx/2x2/3x3/444 accept `{ skipValidation: true }` to skip the per-call verification for maximum throughput (correctness is covered by the test suite, and independently by `pnpm verify:scrambles`, which re-samples each target from its seed).
|
|
63
63
|
|
|
64
64
|
## Scoped imports (web / smaller bundles)
|
|
65
65
|
|
|
@@ -194,7 +194,17 @@ const paired = cube4.areWingsPaired(state);
|
|
|
194
194
|
|
|
195
195
|
Supported moves: `U D R L F B` (outer) and `Uw Dw Rw Lw Fw Bw` (wide), each with `'` and `2` suffixes.
|
|
196
196
|
|
|
197
|
-
The public `generateScramble("444")` path is: sample a uniform random state (`generateRandomCube4State` — corners `8! × 3^7`, wings `24!`, centers as a color multiset `24!/(4!)^6`), solve it with the reduction solver (phases: axis-color face-pairs → ct/rl phase-2 goal → joint center3+pairing → 3x3 finish), and invert the solution — so the scramble always reproduces the sampled target. The pairing pruning table is generated by a C++ tool (`tools/edge3-gen/`).
|
|
197
|
+
The public `generateScramble("444")` path is: sample a uniform random state (`generateRandomCube4State` — corners `8! × 3^7`, wings `24!`, centers as a color multiset `24!/(4!)^6`), solve it with the reduction solver (phases: axis-color face-pairs → ct/rl phase-2 goal → joint center3+pairing → 3x3 finish), and invert the solution — so the scramble always reproduces the sampled target. The production path enforces this on every generation (`assertCube4ScrambleReproducesTarget` re-applies the emitted moves and compares against the state sampled for the seed; a mismatch retries the SAME target — never re-samples, never falls back to random-move). The pairing pruning table is generated by a C++ tool (`tools/edge3-gen/`).
|
|
198
|
+
|
|
199
|
+
How to verify 444, from cheap to heavy:
|
|
200
|
+
|
|
201
|
+
- `pnpm test` — model/round-trip tests, the rotation-sensitive invariant (`test/cube4-solve-invariant.test.ts`), and the uniformity net (`test/cube4-uniformity.test.ts`: χ² over a fixed corpus at two layers — direct sampler N=20,000 and full pipeline N=40 with structural checks on parity quadrants and per-slot diversity).
|
|
202
|
+
- `pnpm verify:scrambles 444` — independent replay: the target is re-sampled from the seed and the solution must genuinely solve it.
|
|
203
|
+
- `node --import tsx scripts/uniformity-444.ts --n=500` — heavy χ² bench over the state **reached** by the pipeline (centers/wings/corners per slot, twist, parity quadrants, joint bins); exits 1 below threshold. The corpora are fixed seeds, so the numbers are deterministic; p-values are reference values, not classical significance.
|
|
204
|
+
|
|
205
|
+
Measured baseline (i5-13420H, warm): 46–53 moves per scramble (avg ≈ 48), latency p50 ≈ 2–3 s / p95 ≈ 9 s, ~37 MB of reduction tables loaded once. Seeded 444 generation is a pure function of `(seed, options)` — search budgets are node counters, not wall deadlines; the only wall-clock code is a pair of hang guards that never bound an observed solve.
|
|
206
|
+
|
|
207
|
+
Shorter 444 scrambles, two mechanisms: `bestOf: N` solves N independently sampled states and keeps the shortest (trades CPU time, biases toward short-solution states), while `restarts: R` re-runs the full pipeline R times over the SAME state with a deterministically reshuffled search order and keeps the shortest total (uniformity-preserving; measured ~-0.45 moves at R=3, ~-0.85 at R=6 on a 24-state corpus, single-attempt mean ~46.1).
|
|
198
208
|
|
|
199
209
|
`validateCube4State` checks the reachability constraints of a normal (non-super) 4x4: corner permutation + orientation sum divisible by 3, wing permutation (wings have no orientation), and 4-of-each-color centers. There is no visible corner/wing parity constraint on a normal 4x4 (all four parity combinations are reachable).
|
|
200
210
|
|
|
@@ -240,9 +250,12 @@ pnpm package:check
|
|
|
240
250
|
pnpm verify
|
|
241
251
|
```
|
|
242
252
|
|
|
243
|
-
Solver tables are versioned as generated binary assets. If cube coordinates, moves, or pruning logic change, regenerate and check them:
|
|
253
|
+
Solver tables are versioned as generated binary assets. If cube coordinates, moves, or pruning logic change, regenerate and check them (the 4x4 reduction tables follow the same pattern, one script per phase):
|
|
244
254
|
|
|
245
255
|
```sh
|
|
246
256
|
pnpm cube3:tables
|
|
247
257
|
pnpm cube3:tables:check
|
|
258
|
+
pnpm cube4:center1:tables:check # likewise center2, center3, edge3, hc
|
|
248
259
|
```
|
|
260
|
+
|
|
261
|
+
`pnpm verify` runs lint, the full test suite, the build, every table checksum check (including all five `cube4:*:tables:check`), and the package checks. `pnpm verify:scrambles [--count N] [events…]` additionally replays generated scrambles against independently re-sampled targets and exits 1 on any mismatch.
|
package/dist/core/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4CrG;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,iBAAmC,GAC5C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,iBAAmC,GAAG,SAAS,EAAE,CAE7F;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,iBAAmC,GAC5C,SAAS,CAQX;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B"}
|
package/dist/core/api.js
CHANGED
|
@@ -39,8 +39,16 @@ export async function initializeScrambler(baseUrl, eventId) {
|
|
|
39
39
|
await loadBrowserTables(baseUrl);
|
|
40
40
|
}
|
|
41
41
|
else if (eventId === "444") {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
// Reduction ends with a 3x3 solve in the same worker. Initializing 444
|
|
43
|
+
// must also load those required tables, including the caller's base URL.
|
|
44
|
+
const [cube4, cube3] = await Promise.all([
|
|
45
|
+
import("../puzzles/cube4/reduction/browser-loader.js"),
|
|
46
|
+
import("../puzzles/cube3/solver/browser-loader.js")
|
|
47
|
+
]);
|
|
48
|
+
await Promise.all([
|
|
49
|
+
cube4.loadCube4ReductionTablesBrowser(baseUrl),
|
|
50
|
+
cube3.loadBrowserTables(baseUrl)
|
|
51
|
+
]);
|
|
44
52
|
}
|
|
45
53
|
else if (!eventId) {
|
|
46
54
|
await Promise.all([
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface Cube2BrowserTables {
|
|
2
2
|
permutationTable: Uint16Array;
|
|
3
3
|
orientationTable: Uint16Array;
|
|
4
4
|
policyTable: Uint8Array;
|
|
5
|
-
}
|
|
5
|
+
}
|
|
6
|
+
export declare function getCachedBrowserTables(): Cube2BrowserTables | undefined;
|
|
6
7
|
export declare function clearCachedBrowserTables(): void;
|
|
7
|
-
export declare function loadBrowserTables(baseUrl?: string): Promise<
|
|
8
|
-
permutationTable: Uint16Array;
|
|
9
|
-
orientationTable: Uint16Array;
|
|
10
|
-
policyTable: Uint8Array;
|
|
11
|
-
}>;
|
|
8
|
+
export declare function loadBrowserTables(baseUrl?: string): Promise<Cube2BrowserTables>;
|
|
12
9
|
//# sourceMappingURL=browser-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/browser-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/browser-loader.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,UAAU,CAAC;CACzB;AAKD,wBAAgB,sBAAsB,IAAI,kBAAkB,GAAG,SAAS,CAEvE;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoBrF"}
|
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import { CUBE2_SOLVER_TABLE_METADATA } from "./table-metadata.js";
|
|
2
2
|
import { fetchTableBuffer } from "../../shared/compressed-fetch.js";
|
|
3
3
|
const TABLES_DIR = "puzzles/cube2/solver/generated";
|
|
4
|
+
const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
|
|
4
5
|
let cachedBrowserTables;
|
|
6
|
+
let cachedBrowserTablesPromise;
|
|
5
7
|
export function getCachedBrowserTables() {
|
|
6
8
|
return cachedBrowserTables;
|
|
7
9
|
}
|
|
8
10
|
export function clearCachedBrowserTables() {
|
|
9
11
|
cachedBrowserTables = undefined;
|
|
12
|
+
cachedBrowserTablesPromise = undefined;
|
|
10
13
|
}
|
|
11
14
|
export async function loadBrowserTables(baseUrl) {
|
|
12
15
|
if (cachedBrowserTables) {
|
|
13
16
|
return cachedBrowserTables;
|
|
14
17
|
}
|
|
18
|
+
if (cachedBrowserTablesPromise) {
|
|
19
|
+
// One shared in-flight promise: concurrent calls trigger exactly one
|
|
20
|
+
// fetch pass (dedup).
|
|
21
|
+
return cachedBrowserTablesPromise;
|
|
22
|
+
}
|
|
23
|
+
const promise = fetchTables(baseUrl);
|
|
24
|
+
cachedBrowserTablesPromise = promise;
|
|
25
|
+
promise.then(() => { }, () => {
|
|
26
|
+
// A failed load must not poison the dedup slot: purge the rejected
|
|
27
|
+
// promise so a later call retries from scratch.
|
|
28
|
+
if (cachedBrowserTablesPromise === promise)
|
|
29
|
+
cachedBrowserTablesPromise = undefined;
|
|
30
|
+
});
|
|
31
|
+
return promise;
|
|
32
|
+
}
|
|
33
|
+
async function fetchTables(baseUrl) {
|
|
15
34
|
const resolved = resolveBaseUrl(baseUrl);
|
|
16
35
|
const manifest = await fetchManifest(resolved);
|
|
17
36
|
const [permTable, orientTable, dist] = await Promise.all([
|
|
18
|
-
fetchUint16Table(resolved, manifest.files.permutationTable, CUBE2_SOLVER_TABLE_METADATA.permutationTable
|
|
19
|
-
fetchUint16Table(resolved, manifest.files.orientationTable, CUBE2_SOLVER_TABLE_METADATA.orientationTable
|
|
20
|
-
fetchUint8Table(resolved, manifest.files.policy, CUBE2_SOLVER_TABLE_METADATA.policy
|
|
37
|
+
fetchUint16Table(resolved, manifest.files.permutationTable, CUBE2_SOLVER_TABLE_METADATA.permutationTable),
|
|
38
|
+
fetchUint16Table(resolved, manifest.files.orientationTable, CUBE2_SOLVER_TABLE_METADATA.orientationTable),
|
|
39
|
+
fetchUint8Table(resolved, manifest.files.policy, CUBE2_SOLVER_TABLE_METADATA.policy)
|
|
21
40
|
]);
|
|
22
41
|
cachedBrowserTables = {
|
|
23
42
|
permutationTable: permTable,
|
|
@@ -51,22 +70,31 @@ async function fetchManifest(baseUrl) {
|
|
|
51
70
|
}
|
|
52
71
|
return manifest;
|
|
53
72
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Fetch + verify with the SAME checks as node-loader.ts: byte length from
|
|
75
|
+
* the manifest entries, FNV-1a checksum against the table-metadata
|
|
76
|
+
* constants (the values node asserts against).
|
|
77
|
+
*/
|
|
78
|
+
async function fetchUint16Table(baseUrl, entry, meta) {
|
|
79
|
+
const buffer = await fetchTableBuffer(baseUrl, TABLES_DIR, entry.file, {
|
|
80
|
+
cacheName: CACHE_NAME,
|
|
81
|
+
compressed: entry.compressed !== false,
|
|
82
|
+
expectedBytes: entry.entries * 2,
|
|
83
|
+
expectedFnv1a: { value: meta.checksum, elementSize: 2 }
|
|
84
|
+
});
|
|
85
|
+
const values = new Uint16Array(entry.entries);
|
|
60
86
|
const view = new DataView(buffer);
|
|
61
87
|
for (let i = 0; i < values.length; i++) {
|
|
62
88
|
values[i] = view.getUint16(i * 2, true);
|
|
63
89
|
}
|
|
64
90
|
return values;
|
|
65
91
|
}
|
|
66
|
-
async function fetchUint8Table(baseUrl, entry,
|
|
67
|
-
const buffer = await fetchTableBuffer(baseUrl, TABLES_DIR, entry.file, {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
92
|
+
async function fetchUint8Table(baseUrl, entry, meta) {
|
|
93
|
+
const buffer = await fetchTableBuffer(baseUrl, TABLES_DIR, entry.file, {
|
|
94
|
+
cacheName: CACHE_NAME,
|
|
95
|
+
compressed: entry.compressed !== false,
|
|
96
|
+
expectedBytes: entry.entries,
|
|
97
|
+
expectedFnv1a: { value: meta.checksum, elementSize: 1 }
|
|
98
|
+
});
|
|
71
99
|
return new Uint8Array(buffer);
|
|
72
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/generated-assets.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/generated-assets.ts"],"names":[],"mappings":"AAkBA,wBAAsB,8BAA8B,IAAI,OAAO,CAAC;IAC9D,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAcD"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { getCachedBrowserTables } from "./browser-loader.js";
|
|
2
|
-
|
|
2
|
+
import { loadOnce } from "../../shared/load-once.js";
|
|
3
|
+
// One shared in-flight promise for the node fs load (same pattern as
|
|
4
|
+
// cube3/solver/generated-assets.ts): the previous `cached ??= await …` raced —
|
|
5
|
+
// concurrent callers interleaving on the await both ran `loadNodeTables()`
|
|
6
|
+
// (two filesystem reads, last assignment wins). On rejection the slot is
|
|
7
|
+
// purged so the next call can retry.
|
|
8
|
+
const nodeTablesSlot = { promise: undefined };
|
|
3
9
|
function isBrowser() {
|
|
4
10
|
return typeof self !== "undefined";
|
|
5
11
|
}
|
|
@@ -13,8 +19,7 @@ export async function loadGeneratedCube2SolverTables() {
|
|
|
13
19
|
"In browser environments, call `initializeScrambler(baseUrl)` " +
|
|
14
20
|
"before generating scrambles.");
|
|
15
21
|
}
|
|
16
|
-
|
|
17
|
-
return cachedTables;
|
|
22
|
+
return loadOnce(nodeTablesSlot, loadNodeTables);
|
|
18
23
|
}
|
|
19
24
|
async function loadNodeTables() {
|
|
20
25
|
const loader = await import(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAezD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAQ/E;AAKD,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGjF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAKtE;AAoDD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAUnG;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAOnG;AAMD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAclG"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSolvedCube2State } from "../state.js";
|
|
2
|
-
import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, POLICY_TABLE, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
|
|
2
|
+
import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, POLICY_TABLE, POLICY_TABLE_EXPANDED, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
|
|
3
3
|
export async function solveCube2State(state) {
|
|
4
4
|
await ensureTablesLoaded();
|
|
5
5
|
const indices = solveWithTablesIndices(state);
|
|
@@ -29,7 +29,7 @@ function solveWithTablesIndices(state) {
|
|
|
29
29
|
}
|
|
30
30
|
const permTable = PERMUTATION_TABLE;
|
|
31
31
|
const orientTable = ORIENTATION_TABLE;
|
|
32
|
-
const policy =
|
|
32
|
+
const policy = POLICY_TABLE_EXPANDED;
|
|
33
33
|
let permIndex = encodePermutation(state.corners.permutation);
|
|
34
34
|
let orientIndex = encodeOrientation(state.corners.orientation);
|
|
35
35
|
const moves = [];
|
|
@@ -37,7 +37,7 @@ function solveWithTablesIndices(state) {
|
|
|
37
37
|
// candidate scanning (the old BFS-distance gradient walk) is needed.
|
|
38
38
|
let combined = permIndex * ORIENTATION_COUNT + orientIndex;
|
|
39
39
|
while (combined !== 0) {
|
|
40
|
-
const m =
|
|
40
|
+
const m = policy[combined];
|
|
41
41
|
moves.push(m);
|
|
42
42
|
permIndex = permTable[permIndex * MOVE_COUNT + m];
|
|
43
43
|
orientIndex = orientTable[orientIndex * MOVE_COUNT + m];
|
|
@@ -6,6 +6,12 @@ export declare function encodeOrientation(orientation: readonly number[]): numbe
|
|
|
6
6
|
export declare let PERMUTATION_TABLE: Uint16Array | null;
|
|
7
7
|
export declare let ORIENTATION_TABLE: Uint16Array | null;
|
|
8
8
|
export declare let POLICY_TABLE: Uint8Array | null;
|
|
9
|
+
/**
|
|
10
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
11
|
+
* the hot walk). Built at load from the packed table: +3.7 MB RAM,
|
|
12
|
+
* zero wire/format change.
|
|
13
|
+
*/
|
|
14
|
+
export declare let POLICY_TABLE_EXPANDED: Uint8Array | null;
|
|
9
15
|
export declare function ensureTablesLoaded(): Promise<void>;
|
|
10
16
|
export declare function areCube2TablesLoaded(): boolean;
|
|
11
17
|
export declare function getDistance(permIndex: number, orientIndex: number): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAW5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxE;AAWD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAW5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxE;AAWD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD;;;;GAIG;AACH,eAAO,IAAI,qBAAqB,EAAE,UAAU,GAAG,IAAW,CAAC;AAE3D,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAcxD;AAUD,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAMD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAItC,CAAC"}
|
|
@@ -54,6 +54,12 @@ let tablesLoaded = false;
|
|
|
54
54
|
export let PERMUTATION_TABLE = null;
|
|
55
55
|
export let ORIENTATION_TABLE = null;
|
|
56
56
|
export let POLICY_TABLE = null;
|
|
57
|
+
/**
|
|
58
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
59
|
+
* the hot walk). Built at load from the packed table: +3.7 MB RAM,
|
|
60
|
+
* zero wire/format change.
|
|
61
|
+
*/
|
|
62
|
+
export let POLICY_TABLE_EXPANDED = null;
|
|
57
63
|
export async function ensureTablesLoaded() {
|
|
58
64
|
if (tablesLoaded) {
|
|
59
65
|
return;
|
|
@@ -62,8 +68,16 @@ export async function ensureTablesLoaded() {
|
|
|
62
68
|
PERMUTATION_TABLE = tables.permutationTable;
|
|
63
69
|
ORIENTATION_TABLE = tables.orientationTable;
|
|
64
70
|
POLICY_TABLE = tables.policyTable;
|
|
71
|
+
POLICY_TABLE_EXPANDED = expandNibblePolicy(tables.policyTable, PERMUTATION_COUNT * ORIENTATION_COUNT);
|
|
65
72
|
tablesLoaded = true;
|
|
66
73
|
}
|
|
74
|
+
function expandNibblePolicy(packed, count) {
|
|
75
|
+
const expanded = new Uint8Array(count);
|
|
76
|
+
for (let index = 0; index < count; index += 1) {
|
|
77
|
+
expanded[index] = (packed[index >> 1] >> ((index & 1) << 2)) & 0x0f;
|
|
78
|
+
}
|
|
79
|
+
return expanded;
|
|
80
|
+
}
|
|
67
81
|
export function areCube2TablesLoaded() {
|
|
68
82
|
return tablesLoaded;
|
|
69
83
|
}
|
|
@@ -71,7 +85,7 @@ export function areCube2TablesLoaded() {
|
|
|
71
85
|
// Public API
|
|
72
86
|
// ---------------------------------------------------------------------------
|
|
73
87
|
export function getDistance(permIndex, orientIndex) {
|
|
74
|
-
if (!
|
|
88
|
+
if (!POLICY_TABLE_EXPANDED) {
|
|
75
89
|
throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
76
90
|
}
|
|
77
91
|
const permTable = PERMUTATION_TABLE;
|
|
@@ -81,7 +95,7 @@ export function getDistance(permIndex, orientIndex) {
|
|
|
81
95
|
let combined = p * ORIENTATION_COUNT + o;
|
|
82
96
|
let distance = 0;
|
|
83
97
|
while (combined !== 0) {
|
|
84
|
-
const m =
|
|
98
|
+
const m = POLICY_TABLE_EXPANDED[combined];
|
|
85
99
|
p = permTable[p * MOVE_COUNT + m];
|
|
86
100
|
o = orientTable[o * MOVE_COUNT + m];
|
|
87
101
|
combined = p * ORIENTATION_COUNT + o;
|
|
@@ -4,6 +4,7 @@ export { cloneCube3State, createSolvedCube3State, cube3StatesEqual, isSolvedCube
|
|
|
4
4
|
export { isAllowedRandomCube3State, isOneMoveFromSolvedCube3State, permutationParity, validateCube3State } from "./validation.js";
|
|
5
5
|
export { generateRandomCube3State } from "./random-state.js";
|
|
6
6
|
export { cube3ScrambleGenerator, generateCube3Scramble } from "./scrambler.js";
|
|
7
|
+
export type { Cube3ScrambleOptions } from "./scrambler.js";
|
|
7
8
|
export { formatCube3Facelets, getCube3FaceOrder, toCube3Facelets } from "./facelets.js";
|
|
8
9
|
export * as solver from "./solver/index.js";
|
|
9
10
|
export type { Cube3Face, Cube3Facelets, Cube3Move, Cube3FaceMove, Cube3MoveSuffix, Cube3RandomStateOptions, Cube3Sticker, Cube3State, Cube3ValidationResult } from "./types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;AAyGxF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAsB7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA+B9F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAoFlE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAQ1D"}
|
|
@@ -26,16 +26,114 @@ const FACE_MOVE_TABLES = {
|
|
|
26
26
|
edges: { permutation: [0, 1, 2, 11, 4, 5, 6, 10, 8, 9, 3, 7], orientation: [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1] }
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
-
|
|
29
|
+
function quarterEffect(face) {
|
|
30
|
+
const table = FACE_MOVE_TABLES[face];
|
|
31
|
+
return {
|
|
32
|
+
cornerPermutation: table.corners.permutation,
|
|
33
|
+
cornerOrientation: table.corners.orientation,
|
|
34
|
+
edgePermutation: table.edges.permutation,
|
|
35
|
+
edgeOrientation: table.edges.orientation
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Compose two effects (apply `first`, then `second`) into one so a move of
|
|
39
|
+
// any suffix costs exactly one pass over the state — no per-quarter-turn
|
|
40
|
+
// cloning, no intermediate states, no closure allocations.
|
|
41
|
+
function composeEffects(first, second) {
|
|
42
|
+
const cornerPermutation = new Array(8);
|
|
43
|
+
const cornerOrientation = new Array(8);
|
|
44
|
+
const edgePermutation = new Array(12);
|
|
45
|
+
const edgeOrientation = new Array(12);
|
|
46
|
+
for (let i = 0; i < 8; i++) {
|
|
47
|
+
const source = second.cornerPermutation[i];
|
|
48
|
+
cornerPermutation[i] = first.cornerPermutation[source];
|
|
49
|
+
cornerOrientation[i] = (first.cornerOrientation[source] + second.cornerOrientation[i]) % 3;
|
|
50
|
+
}
|
|
51
|
+
for (let i = 0; i < 12; i++) {
|
|
52
|
+
const source = second.edgePermutation[i];
|
|
53
|
+
edgePermutation[i] = first.edgePermutation[source];
|
|
54
|
+
edgeOrientation[i] = (first.edgeOrientation[source] + second.edgeOrientation[i]) % 2;
|
|
55
|
+
}
|
|
56
|
+
return { cornerPermutation, cornerOrientation, edgePermutation, edgeOrientation };
|
|
57
|
+
}
|
|
58
|
+
function powerEffect(face, turns) {
|
|
59
|
+
const quarter = quarterEffect(face);
|
|
60
|
+
if (turns === 1)
|
|
61
|
+
return quarter;
|
|
62
|
+
const half = composeEffects(quarter, quarter);
|
|
63
|
+
if (turns === 2)
|
|
64
|
+
return half;
|
|
65
|
+
return composeEffects(half, quarter);
|
|
66
|
+
}
|
|
67
|
+
// Total effect per move string, built once at module load (mirrors the 4x4
|
|
68
|
+
// fast path): one lookup + one pass per move.
|
|
69
|
+
const COMPOSED_EFFECTS = Object.fromEntries(CUBE3_MOVES.map((move) => {
|
|
70
|
+
const face = move[0];
|
|
71
|
+
const suffix = move.slice(1);
|
|
72
|
+
const turns = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
|
|
73
|
+
return [move, powerEffect(face, turns)];
|
|
74
|
+
}));
|
|
75
|
+
function effectForMove(move) {
|
|
76
|
+
const hit = COMPOSED_EFFECTS[move];
|
|
77
|
+
if (hit !== undefined)
|
|
78
|
+
return hit;
|
|
30
79
|
const { face, turns } = parseMoveParts(move);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
80
|
+
return powerEffect(face, turns);
|
|
81
|
+
}
|
|
82
|
+
export function applyCube3Move(state, move) {
|
|
83
|
+
const effect = effectForMove(move);
|
|
84
|
+
const cornerPermutation = new Array(8);
|
|
85
|
+
const cornerOrientation = new Array(8);
|
|
86
|
+
const edgePermutation = new Array(12);
|
|
87
|
+
const edgeOrientation = new Array(12);
|
|
88
|
+
for (let i = 0; i < 8; i++) {
|
|
89
|
+
const source = effect.cornerPermutation[i];
|
|
90
|
+
cornerPermutation[i] = state.corners.permutation[source];
|
|
91
|
+
cornerOrientation[i] =
|
|
92
|
+
(state.corners.orientation[source] + effect.cornerOrientation[i]) % 3;
|
|
34
93
|
}
|
|
35
|
-
|
|
94
|
+
for (let i = 0; i < 12; i++) {
|
|
95
|
+
const source = effect.edgePermutation[i];
|
|
96
|
+
edgePermutation[i] = state.edges.permutation[source];
|
|
97
|
+
edgeOrientation[i] =
|
|
98
|
+
(state.edges.orientation[source] + effect.edgeOrientation[i]) % 2;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
corners: { permutation: cornerPermutation, orientation: cornerOrientation },
|
|
102
|
+
edges: { permutation: edgePermutation, orientation: edgeOrientation }
|
|
103
|
+
};
|
|
36
104
|
}
|
|
37
105
|
export function applyCube3Algorithm(state, moves) {
|
|
38
|
-
|
|
106
|
+
if (moves.length === 0)
|
|
107
|
+
return cloneCube3State(state);
|
|
108
|
+
let curCp = [...state.corners.permutation];
|
|
109
|
+
let curCo = [...state.corners.orientation];
|
|
110
|
+
let curEp = [...state.edges.permutation];
|
|
111
|
+
let curEo = [...state.edges.orientation];
|
|
112
|
+
let nxtCp = new Array(8);
|
|
113
|
+
let nxtCo = new Array(8);
|
|
114
|
+
let nxtEp = new Array(12);
|
|
115
|
+
let nxtEo = new Array(12);
|
|
116
|
+
for (const move of moves) {
|
|
117
|
+
const effect = effectForMove(move);
|
|
118
|
+
for (let i = 0; i < 8; i++) {
|
|
119
|
+
const source = effect.cornerPermutation[i];
|
|
120
|
+
nxtCp[i] = curCp[source];
|
|
121
|
+
nxtCo[i] = (curCo[source] + effect.cornerOrientation[i]) % 3;
|
|
122
|
+
}
|
|
123
|
+
for (let i = 0; i < 12; i++) {
|
|
124
|
+
const source = effect.edgePermutation[i];
|
|
125
|
+
nxtEp[i] = curEp[source];
|
|
126
|
+
nxtEo[i] = (curEo[source] + effect.edgeOrientation[i]) % 2;
|
|
127
|
+
}
|
|
128
|
+
[curCp, nxtCp] = [nxtCp, curCp];
|
|
129
|
+
[curCo, nxtCo] = [nxtCo, curCo];
|
|
130
|
+
[curEp, nxtEp] = [nxtEp, curEp];
|
|
131
|
+
[curEo, nxtEo] = [nxtEo, curEo];
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
corners: { permutation: curCp, orientation: curCo },
|
|
135
|
+
edges: { permutation: curEp, orientation: curEo }
|
|
136
|
+
};
|
|
39
137
|
}
|
|
40
138
|
export function invertCube3Algorithm(moves) {
|
|
41
139
|
return [...moves].reverse().map(invertCube3Move);
|
|
@@ -190,18 +288,6 @@ export function invertCube3Move(move) {
|
|
|
190
288
|
}
|
|
191
289
|
return `${face}${suffix === "'" ? "" : "'"}`;
|
|
192
290
|
}
|
|
193
|
-
function applyFaceMove(state, table) {
|
|
194
|
-
return {
|
|
195
|
-
corners: {
|
|
196
|
-
permutation: table.corners.permutation.map((source) => state.corners.permutation[source]),
|
|
197
|
-
orientation: table.corners.permutation.map((source, target) => ((state.corners.orientation[source] + table.corners.orientation[target]) % 3))
|
|
198
|
-
},
|
|
199
|
-
edges: {
|
|
200
|
-
permutation: table.edges.permutation.map((source) => state.edges.permutation[source]),
|
|
201
|
-
orientation: table.edges.permutation.map((source, target) => ((state.edges.orientation[source] + table.edges.orientation[target]) % 2))
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
291
|
function parseMoveParts(move) {
|
|
206
292
|
const face = move[0];
|
|
207
293
|
const suffix = move.slice(1);
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import type { ScrambleGenerator, ScrambleOptions, ScrambleResult } from "../../core/index.js";
|
|
2
|
+
import type { Cube3SolveOptions } from "./solver/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* 333-specific scramble options (pass-through; extra keys are ignored by
|
|
5
|
+
* the generic `generateScramble` API surface).
|
|
6
|
+
*/
|
|
7
|
+
export interface Cube3ScrambleOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Solver tuning. Tiers (browser tables): `fast` (~14 ms, ~23.1 moves),
|
|
10
|
+
* `short` default (~36 ms, ~21.9 moves), `best` (~116 ms, ~20.8 moves,
|
|
11
|
+
* max 22). Granular overrides: `phase1ExtraDepth`,
|
|
12
|
+
* `phase1SolutionLimit`, `improvePhase2TimeoutMs`, `orientations`
|
|
13
|
+
* (1-24), `targetLength`, `timeoutMs`.
|
|
14
|
+
*/
|
|
15
|
+
solver?: Cube3SolveOptions;
|
|
16
|
+
}
|
|
2
17
|
export declare const cube3ScrambleGenerator: ScrambleGenerator;
|
|
3
|
-
export declare function generateCube3Scramble(options?: ScrambleOptions & {
|
|
18
|
+
export declare function generateCube3Scramble(options?: ScrambleOptions & Cube3ScrambleOptions & {
|
|
4
19
|
skipValidation?: boolean;
|
|
5
20
|
}): Promise<ScrambleResult>;
|
|
6
21
|
//# sourceMappingURL=scrambler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAU9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG,oBAAoB,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAClF,OAAO,CAAC,cAAc,CAAC,CAgDzB"}
|
|
@@ -18,7 +18,7 @@ export const cube3ScrambleGenerator = {
|
|
|
18
18
|
};
|
|
19
19
|
export async function generateCube3Scramble(options = {}) {
|
|
20
20
|
const state = generateRandomCube3State(options);
|
|
21
|
-
const solution = await solveCube3State(state);
|
|
21
|
+
const solution = await solveCube3State(state, options.solver ?? {});
|
|
22
22
|
const scrambleMoves = invertCube3Algorithm(solution.moves);
|
|
23
23
|
if (!options.skipValidation) {
|
|
24
24
|
const scrambledState = applyCube3Algorithm(createSolvedCube3State(), scrambleMoves);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/browser-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAMV,iBAAiB,EAClB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/browser-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAMV,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAuDrB,wBAAgB,sBAAsB,IAAI,iBAAiB,GAAG,SAAS,CAEtE;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBpF"}
|