@cubingcenter/scrambler 0.6.3 → 0.7.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.
Files changed (115) hide show
  1. package/README.md +2 -0
  2. package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
  3. package/dist/puzzles/cube2/solver/search.js +3 -3
  4. package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
  5. package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
  6. package/dist/puzzles/cube2/solver/tables.js +16 -2
  7. package/dist/puzzles/cube3/index.d.ts +1 -0
  8. package/dist/puzzles/cube3/index.d.ts.map +1 -1
  9. package/dist/puzzles/cube3/moves.d.ts.map +1 -1
  10. package/dist/puzzles/cube3/moves.js +104 -18
  11. package/dist/puzzles/cube3/scrambler.d.ts +16 -1
  12. package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
  13. package/dist/puzzles/cube3/scrambler.js +1 -1
  14. package/dist/puzzles/cube3/solver/browser-loader.js +6 -2
  15. package/dist/puzzles/cube3/solver/index.d.ts +5 -3
  16. package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
  17. package/dist/puzzles/cube3/solver/index.js +2 -1
  18. package/dist/puzzles/cube3/solver/node-loader.js +9 -2
  19. package/dist/puzzles/cube3/solver/phase1.d.ts +18 -0
  20. package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
  21. package/dist/puzzles/cube3/solver/phase1.js +132 -14
  22. package/dist/puzzles/cube3/solver/phase2.d.ts +47 -0
  23. package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
  24. package/dist/puzzles/cube3/solver/phase2.js +236 -66
  25. package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
  26. package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
  27. package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
  28. package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
  29. package/dist/puzzles/cube3/solver/symmetry.js +74 -0
  30. package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
  31. package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
  32. package/dist/puzzles/cube3/validation.d.ts.map +1 -1
  33. package/dist/puzzles/cube3/validation.js +15 -7
  34. package/dist/puzzles/cube4/index.d.ts +3 -2
  35. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  36. package/dist/puzzles/cube4/index.js +2 -2
  37. package/dist/puzzles/cube4/moves.d.ts +5 -0
  38. package/dist/puzzles/cube4/moves.d.ts.map +1 -1
  39. package/dist/puzzles/cube4/moves.js +120 -11
  40. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +3 -0
  41. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
  42. package/dist/puzzles/cube4/reduction/browser-loader.js +34 -3
  43. package/dist/puzzles/cube4/reduction/center2.d.ts +20 -3
  44. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
  45. package/dist/puzzles/cube4/reduction/center2.js +53 -9
  46. package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
  47. package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
  48. package/dist/puzzles/cube4/reduction/edge3.js +23 -3
  49. package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  50. package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  51. package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  52. package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  53. package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
  54. package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  55. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +50 -0
  56. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
  57. package/dist/puzzles/cube4/reduction/hc-pdb.js +202 -0
  58. package/dist/puzzles/cube4/reduction/node-loader.d.ts +5 -0
  59. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
  60. package/dist/puzzles/cube4/reduction/node-loader.js +48 -8
  61. package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  62. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +111 -5
  63. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
  64. package/dist/puzzles/cube4/reduction/phase3-search.js +341 -22
  65. package/dist/puzzles/cube4/reduction/solve.d.ts +112 -1
  66. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
  67. package/dist/puzzles/cube4/reduction/solve.js +920 -181
  68. package/dist/puzzles/cube4/scrambler.d.ts +105 -4
  69. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  70. package/dist/puzzles/cube4/scrambler.js +216 -166
  71. package/dist/puzzles/cube4/state.d.ts.map +1 -1
  72. package/dist/puzzles/cube4/state.js +4 -1
  73. package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
  74. package/dist/puzzles/pyraminx/solver/search.js +3 -6
  75. package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
  76. package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
  77. package/dist/puzzles/pyraminx/solver/tables.js +17 -0
  78. package/dist/puzzles/skewb/solver/search.js +3 -3
  79. package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
  80. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
  81. package/dist/puzzles/skewb/solver/tables.js +17 -0
  82. package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
  83. package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
  84. package/dist-cjs/puzzles/cube3/moves.js +104 -18
  85. package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
  86. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +6 -2
  87. package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
  88. package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
  89. package/dist-cjs/puzzles/cube3/solver/phase1.js +133 -14
  90. package/dist-cjs/puzzles/cube3/solver/phase2.js +233 -63
  91. package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
  92. package/dist-cjs/puzzles/cube3/validation.js +15 -7
  93. package/dist-cjs/puzzles/cube4/index.js +3 -1
  94. package/dist-cjs/puzzles/cube4/moves.js +121 -11
  95. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +34 -3
  96. package/dist-cjs/puzzles/cube4/reduction/center2.js +54 -9
  97. package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
  98. package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  99. package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  100. package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  101. package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  102. package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
  103. package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  104. package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +213 -0
  105. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +48 -8
  106. package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  107. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +343 -23
  108. package/dist-cjs/puzzles/cube4/reduction/solve.js +918 -173
  109. package/dist-cjs/puzzles/cube4/scrambler.js +214 -163
  110. package/dist-cjs/puzzles/cube4/state.js +4 -1
  111. package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
  112. package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
  113. package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
  114. package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
  115. package/package.json +14 -5
@@ -1,8 +1,9 @@
1
1
  export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
2
2
  export type { Cube4Face, Cube4FaceMove, Cube4Move, Cube4MoveSuffix, Cube4State, Cube4Sticker, Cube4Facelets, Cube4ValidationResult, Cube4RandomStateOptions } from "./types.js";
3
3
  export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
4
- export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
5
- export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
4
+ export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
5
+ export type { Cube4ScrambleOptions } from "./scrambler.js";
6
+ export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
6
7
  export { generateRandomCube4State } from "./random-state.js";
7
8
  export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
8
9
  export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,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/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
@@ -6,8 +6,8 @@ import { cube4ScrambleGenerator } from "./scrambler.js";
6
6
  registerScrambleGenerator(cube4ScrambleGenerator);
7
7
  export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
8
8
  export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
9
- export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
10
- export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
9
+ export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
10
+ export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
11
11
  export { generateRandomCube4State } from "./random-state.js";
12
12
  export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
13
13
  export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
@@ -33,5 +33,10 @@ export declare function invertCube4Algorithm(moves: readonly Cube4Move[]): Cube4
33
33
  export declare function formatCube4Algorithm(moves: readonly Cube4Move[]): string;
34
34
  export declare function parseCube4Algorithm(algorithm: string): Cube4Move[];
35
35
  export declare function parseCube4Move(value: string): Cube4Move;
36
+ /**
37
+ * Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
38
+ * turns on the same face or across commuting parallel faces.
39
+ */
40
+ export declare function simplifyCube4Algorithm(moves: readonly Cube4Move[]): Cube4Move[];
36
41
  export { FACE_MOVE_EFFECTS };
37
42
  //# sourceMappingURL=moves.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAQxF,UAAU,UAAU;IAClB,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA0KD,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,CAGxD,CAAC;AA0BF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc1G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAS7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CAM9F;AAMD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAI1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAMD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AAMD,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxF,UAAU,UAAU;IAClB,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA0KD,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,CAGxD,CAAC;AAuDF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc1G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAK7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA4C9F;AAKD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAI1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAMD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AAkCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAsB/E;AAMD,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -158,6 +158,32 @@ const FACE_MOVE_EFFECTS = {
158
158
  Uw: WIDE_Uw, Dw: WIDE_Dw, Rw: WIDE_Rw, Lw: WIDE_Lw, Fw: WIDE_Fw, Bw: WIDE_Bw
159
159
  };
160
160
  // ---------------------------------------------------------------------------
161
+ // Precomposed total effect per move string ("R'" = R x3, "U2" = U x2),
162
+ // built once at module load. applyCube4Algorithm then costs one lookup +
163
+ // one pass per move: no per-move string parsing, no repeated
164
+ // quarter-turn passes (average ~2 passes collapse into exactly 1).
165
+ // ---------------------------------------------------------------------------
166
+ function composePower(face, turns) {
167
+ const quarter = FACE_MOVE_EFFECTS[face];
168
+ if (turns === 2)
169
+ return composeEffects(quarter, quarter);
170
+ if (turns === 3)
171
+ return composeEffects(composeEffects(quarter, quarter), quarter);
172
+ return quarter;
173
+ }
174
+ const COMPOSED_EFFECTS = Object.fromEntries(CUBE4_ALL_MOVES.map((move) => {
175
+ const { face, turns } = parseMove(move);
176
+ return [move, composePower(face, turns)];
177
+ }));
178
+ /** Total effect for one move string (precomposed fast path + parse fallback). */
179
+ function effectForMove(move) {
180
+ const hit = COMPOSED_EFFECTS[move];
181
+ if (hit !== undefined)
182
+ return hit;
183
+ const { face, turns } = parseMove(move);
184
+ return composePower(face, turns);
185
+ }
186
+ // ---------------------------------------------------------------------------
161
187
  // Apply a single move to a state
162
188
  // ---------------------------------------------------------------------------
163
189
  function applyEffect(state, effect) {
@@ -192,20 +218,52 @@ export function parseMove(move) {
192
218
  return { face: face, suffix, turns };
193
219
  }
194
220
  export function applyCube4Move(state, move) {
195
- const { face, turns } = parseMove(move);
196
- const effect = FACE_MOVE_EFFECTS[face];
197
- let current = state;
198
- for (let i = 0; i < turns; i++) {
199
- current = applyEffect(current, effect);
200
- }
201
- return current;
221
+ // Single-pass fast path through the precomposed total effect: one lookup
222
+ // + one pass, no string re-parsing, no repeated quarter-turn passes, no
223
+ // intermediate states.
224
+ return applyEffect(state, effectForMove(move));
202
225
  }
203
226
  export function applyCube4Algorithm(state, moves) {
204
- let current = state;
205
- for (const move of moves) {
206
- current = applyCube4Move(current, move);
227
+ if (moves.length === 0)
228
+ return state;
229
+ const bufA = {
230
+ cp: new Uint8Array(state.cp),
231
+ co: new Uint8Array(state.co),
232
+ wp: new Uint8Array(state.wp),
233
+ centers: new Uint8Array(state.centers)
234
+ };
235
+ const bufB = {
236
+ cp: new Uint8Array(8),
237
+ co: new Uint8Array(8),
238
+ wp: new Uint8Array(24),
239
+ centers: new Uint8Array(24)
240
+ };
241
+ let cur = bufA;
242
+ let nxt = bufB;
243
+ for (let mIdx = 0; mIdx < moves.length; mIdx++) {
244
+ const move = moves[mIdx];
245
+ const eff = effectForMove(move);
246
+ for (let i = 0; i < 8; i++) {
247
+ const from = eff.cperm[i];
248
+ nxt.cp[i] = cur.cp[from];
249
+ nxt.co[i] = (cur.co[from] + eff.corient[i]) % 3;
250
+ }
251
+ for (let i = 0; i < 24; i++) {
252
+ nxt.wp[i] = cur.wp[eff.wperm[i]];
253
+ }
254
+ for (let i = 0; i < 24; i++) {
255
+ nxt.centers[i] = cur.centers[eff.ctperm[i]];
256
+ }
257
+ const tmp = cur;
258
+ cur = nxt;
259
+ nxt = tmp;
207
260
  }
208
- return current;
261
+ return {
262
+ cp: cur.cp,
263
+ co: cur.co,
264
+ wp: cur.wp,
265
+ centers: cur.centers
266
+ };
209
267
  }
210
268
  // ---------------------------------------------------------------------------
211
269
  // Move inversion
@@ -237,6 +295,57 @@ export function parseCube4Move(value) {
237
295
  }
238
296
  throw new Error(`Invalid 4x4 move: ${value}`);
239
297
  }
298
+ function parse4Move(m) {
299
+ const match = m.match(/^([UDFBLR]w?)(2|')?$/);
300
+ if (!match)
301
+ throw new Error(`Invalid move format: ${m}`);
302
+ const base = match[1];
303
+ const suffix = match[2] ?? "";
304
+ const amount = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
305
+ const axis = (base[0] === "U" || base[0] === "D") ? 0 :
306
+ (base[0] === "R" || base[0] === "L") ? 1 : 2;
307
+ return { base, axis, amount };
308
+ }
309
+ function format4Move(base, amount) {
310
+ const amt = ((amount % 4) + 4) % 4;
311
+ if (amt === 0)
312
+ return null;
313
+ const suffix = amt === 1 ? "" : amt === 2 ? "2" : "'";
314
+ return (base + suffix);
315
+ }
316
+ function movesCommute4(a, b) {
317
+ return a.axis === b.axis && a.base !== b.base;
318
+ }
319
+ /**
320
+ * Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
321
+ * turns on the same face or across commuting parallel faces.
322
+ */
323
+ export function simplifyCube4Algorithm(moves) {
324
+ // Single left-to-right pass with an output stack: each move merges with
325
+ // the nearest same-base entry across trailing commuting parallel-face
326
+ // moves (verified equivalent to the old restart-from-zero loop on 20000
327
+ // random algorithms). O(n·window) instead of O(n²) restarts with splices.
328
+ const out = [];
329
+ for (const move of moves) {
330
+ const current = parse4Move(move);
331
+ let index = out.length - 1;
332
+ while (index >= 0 && movesCommute4(out[index], current))
333
+ index--;
334
+ if (index >= 0 && out[index].base === current.base) {
335
+ const merged = (out[index].amount + current.amount) % 4;
336
+ if (merged === 0)
337
+ out.splice(index, 1);
338
+ else
339
+ out[index].amount = merged;
340
+ }
341
+ else {
342
+ out.push(current);
343
+ }
344
+ }
345
+ return out
346
+ .map((p) => format4Move(p.base, p.amount))
347
+ .filter((m) => m !== null);
348
+ }
240
349
  // ---------------------------------------------------------------------------
241
350
  // Export the raw effects for use by the reduction solver
242
351
  // ---------------------------------------------------------------------------
@@ -6,11 +6,14 @@ import type { Center1Tables } from "./center1.js";
6
6
  import type { Center2Tables } from "./center2.js";
7
7
  import type { Center3Tables } from "./center3.js";
8
8
  import type { Edge3Tables } from "./edge3.js";
9
+ import { type HcDenseTables } from "./hc-pdb.js";
9
10
  export interface Cube4ReductionTables {
10
11
  center1: Center1Tables;
11
12
  center2: Center2Tables;
12
13
  center3: Center3Tables;
13
14
  edge3: Edge3Tables;
15
+ /** Half-class exact PDB distances (optional; absent = bound disabled). */
16
+ hc?: HcDenseTables;
14
17
  }
15
18
  export declare function getCachedCube4Tables(): Cube4ReductionTables | undefined;
16
19
  export declare function clearCachedCube4Tables(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/browser-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;CACpB;AAOD,wBAAgB,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAEvE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,wBAAsB,+BAA+B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAarG"}
1
+ {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/browser-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,aAAa,CAAC;CACpB;AAOD,wBAAgB,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAEvE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,wBAAsB,+BAA+B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAerG"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { edge3DistanceLookup } from "./edge3.js";
6
6
  import { fetchTableBuffer } from "../../shared/compressed-fetch.js";
7
+ import { buildHcDense } from "./hc-pdb.js";
7
8
  const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
8
9
  const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
9
10
  let cachedTables;
@@ -31,13 +32,16 @@ export async function loadCube4ReductionTablesBrowser(baseUrl) {
31
32
  return cachedTables;
32
33
  }
33
34
  const resolved = resolveBaseUrl(baseUrl);
34
- const [center1, center2, center3, edge3] = await Promise.all([
35
+ const [center1, center2, center3, edge3, hc] = await Promise.all([
35
36
  loadCenter1(resolved),
36
37
  loadCenter2(resolved),
37
38
  loadCenter3(resolved),
38
- loadEdge3(resolved)
39
+ loadEdge3(resolved),
40
+ loadHc(resolved)
39
41
  ]);
40
42
  cachedTables = { center1, center2, center3, edge3 };
43
+ if (hc)
44
+ cachedTables.hc = hc;
41
45
  return cachedTables;
42
46
  }
43
47
  async function fetchJson(baseUrl, file) {
@@ -74,7 +78,18 @@ async function loadCenter2(baseUrl) {
74
78
  const byte = pruningPacked[i >> 1];
75
79
  pruning[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
76
80
  }
77
- return { ctMove, rlMove, pruning };
81
+ const tables = { ctMove, rlMove, pruning };
82
+ if (manifest.pruningWideTable) {
83
+ const wideBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.pruningWideTable.file, { cacheName: CACHE_NAME, compressed: manifest.pruningWideTable.compressed !== false });
84
+ const widePacked = new Uint8Array(wideBuffer);
85
+ const pruningWide = new Uint8Array(450450);
86
+ for (let i = 0; i < 450450; i++) {
87
+ const byte = widePacked[i >> 1];
88
+ pruningWide[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
89
+ }
90
+ tables.pruningWide = pruningWide;
91
+ }
92
+ return tables;
78
93
  }
79
94
  async function loadCenter3(baseUrl) {
80
95
  const manifest = await fetchJson(baseUrl, "center3-manifest.json");
@@ -90,6 +105,22 @@ async function loadCenter3(baseUrl) {
90
105
  async function loadEdge3(baseUrl) {
91
106
  const manifest = await fetchJson(baseUrl, "edge3-manifest.json");
92
107
  const distancesBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distances.file, { cacheName: CACHE_NAME, compressed: manifest.distances.compressed !== false });
108
+ // Keep nibble-packed (see node-loader.ts): all readers handle both formats.
93
109
  const distances = new Uint8Array(distancesBuffer);
94
110
  return { distances, distanceOfEdge: edge3DistanceLookup(distances) };
95
111
  }
112
+ async function loadHc(baseUrl) {
113
+ try {
114
+ const manifest = await fetchJson(baseUrl, "hc-manifest.json");
115
+ const distancesBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distances.file, { cacheName: CACHE_NAME, compressed: manifest.distances.compressed !== false });
116
+ const dense = buildHcDense(new Uint8Array(distancesBuffer));
117
+ if (manifest.moveTable) {
118
+ const moveBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.moveTable.file, { cacheName: CACHE_NAME, compressed: manifest.moveTable.compressed !== false });
119
+ dense.moves = new Int32Array(moveBuffer);
120
+ }
121
+ return dense;
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ }
@@ -12,7 +12,9 @@
12
12
  * - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
13
13
  * - the 28-move set keeps the ct and rl position sets separate, so the move
14
14
  * tables factor: ctmv (6,435×28) and rlmv (70×28);
15
- * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw').
15
+ * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
16
+ * by default; the wide set (`CENTER2_SEARCH_MOVES_WIDE`, all 28, with
17
+ * the `pruningWide` table) additionally tries Dw2/Lw/Lw2/Lw'/Bw2.
16
18
  *
17
19
  * The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
18
20
  */
@@ -21,6 +23,12 @@ import type { Cube4State, Cube4Move } from "../types.js";
21
23
  export declare const CENTER2_MOVES: readonly Cube4Move[];
22
24
  /** The search's exploration set (the first 23 of CENTER2_MOVES). */
23
25
  export declare const CENTER2_SEARCH_MOVES: readonly Cube4Move[];
26
+ /**
27
+ * The wide exploration set: all 28 moves (adds Dw2/Lw/Lw2/Lw'/Bw2).
28
+ * Searches over this set must use the `pruningWide` table — the
29
+ * 23-move pruning distances are inadmissible here (they overestimate).
30
+ */
31
+ export declare const CENTER2_SEARCH_MOVES_WIDE: readonly Cube4Move[];
24
32
  /** The 6 reference goal rl patterns (full 70-space values, even parity). */
25
33
  export declare const CENTER2_GOAL_RL: readonly number[];
26
34
  export declare const CENTER2_CT_STATES = 6435;
@@ -34,9 +42,12 @@ export declare function wingPermutationParity(wp: Uint8Array): 0 | 1;
34
42
  /** Encode a full 4x4 state to the (ct, rl) coordinate (0..450,449). */
35
43
  export declare function encodeCenter2(state: Cube4State): Center2Encoded;
36
44
  export declare function isCenter2Goal(ct: number, rl: number): boolean;
45
+ export declare const ALIGNMENT_ROTATIONS: readonly (readonly string[])[];
37
46
  export interface Center2BridgeResult {
38
47
  /** Alignment moves (0-2). */
39
48
  alignment: Cube4Move[];
49
+ /** Alignment rotation sequence (0-1 rotations: [], ["z"], or ["y"]). */
50
+ rotation: string[];
40
51
  /** The aligned state (invariant). */
41
52
  state: Cube4State;
42
53
  /** The (ct, rl) coordinate of the aligned state. */
@@ -57,8 +68,13 @@ export interface Center2Tables {
57
68
  ctMove: Int32Array;
58
69
  /** 70 × 28 — rl rank (parity included) after each move. */
59
70
  rlMove: Int32Array;
60
- /** 450,450 exact BFS distances from the goal. */
71
+ /** 450,450 exact BFS distances from the goal (23-move set). */
61
72
  pruning: Uint8Array;
73
+ /**
74
+ * 450,450 exact BFS distances from the goal (wide 28-move set).
75
+ * Optional in memory (older assets); required for wide searches.
76
+ */
77
+ pruningWide?: Uint8Array | undefined;
62
78
  }
63
79
  export declare function generateCenter2Tables(): Center2Tables;
64
80
  export interface Center2Phase2Result {
@@ -77,6 +93,7 @@ export declare function solveCenter2(startCt: number, startRl: number, tables: C
77
93
  * (transversal + even wing permutation), which most states fail.
78
94
  */
79
95
  export declare function solveCenter2All(startCt: number, startRl: number, tables: Center2Tables, maxLength?: number, cap?: number): Center2Phase2Result[];
96
+ export declare function getWingMoveMaps(): Int32Array;
80
97
  /**
81
98
  * Solve Center 2 gated for an exact target length.
82
99
  */
@@ -87,5 +104,5 @@ export declare function solveCenter2GatedLength(startCt: number, startRl: number
87
104
  * transversal (the phase-3 entry gate).
88
105
  */
89
106
  export declare function solveCenter2Gated(startCt: number, startRl: number, startWp: Uint8Array, tables: Center2Tables, isGateEligible: (wp: Uint8Array) => boolean, maxLength?: number): Center2Phase2Result | null;
90
- export declare function verifyCenter2(tables: Center2Tables): string[];
107
+ export declare function verifyCenter2(tables: Center2Tables, pruning?: Uint8Array, moves?: readonly Cube4Move[]): string[];
91
108
  //# sourceMappingURL=center2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"center2.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAOzD,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAIpC,CAAC;AAEX,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAA+B,CAAC;AAErF,4EAA4E;AAC5E,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAA4B,CAAC;AAE1E,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,cAAc,QAAwC,CAAC;AA6BpE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAYD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,CAe3D;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CAQ/D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7D;AA4BD,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,qCAAqC;IACrC,KAAK,EAAE,UAAU,CAAC;IAClB,oDAAoD;IACpD,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,GAAG,IAAI,CAe3E;AAsBD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,MAAM,EAAE,UAAU,CAAC;IACnB,2DAA2D;IAC3D,MAAM,EAAE,UAAU,CAAC;IACnB,iDAAiD;IACjD,OAAO,EAAE,UAAU,CAAC;CACrB;AAeD,wBAAgB,qBAAqB,IAAI,aAAa,CAoErD;AAMD,MAAM,WAAW,mBAAmB;IAClC,mDAAmD;IACnD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB;AA+BD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,EACrB,SAAS,SAAK,GACb,mBAAmB,GAAG,IAAI,CAG5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,EACrB,SAAS,SAAK,EACd,GAAG,SAAM,GACR,mBAAmB,EAAE,CAkCvB;AAyBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,EAC3C,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,MAAM,EAAoC,GAClD,mBAAmB,GAAG,IAAI,CA2C5B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,EAC3C,SAAS,SAAK,GACb,mBAAmB,GAAG,IAAI,CAgB5B;AAsBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA2B7D"}
1
+ {"version":3,"file":"center2.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center2.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,9 @@
12
12
  * - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
13
13
  * - the 28-move set keeps the ct and rl position sets separate, so the move
14
14
  * tables factor: ctmv (6,435×28) and rlmv (70×28);
15
- * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw').
15
+ * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
16
+ * by default; the wide set (`CENTER2_SEARCH_MOVES_WIDE`, all 28, with
17
+ * the `pruningWide` table) additionally tries Dw2/Lw/Lw2/Lw'/Bw2.
16
18
  *
17
19
  * The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
18
20
  */
@@ -28,6 +30,12 @@ export const CENTER2_MOVES = [
28
30
  ];
29
31
  /** The search's exploration set (the first 23 of CENTER2_MOVES). */
30
32
  export const CENTER2_SEARCH_MOVES = CENTER2_MOVES.slice(0, 23);
33
+ /**
34
+ * The wide exploration set: all 28 moves (adds Dw2/Lw/Lw2/Lw'/Bw2).
35
+ * Searches over this set must use the `pruningWide` table — the
36
+ * 23-move pruning distances are inadmissible here (they overestimate).
37
+ */
38
+ export const CENTER2_SEARCH_MOVES_WIDE = CENTER2_MOVES;
31
39
  /** The 6 reference goal rl patterns (full 70-space values, even parity). */
32
40
  export const CENTER2_GOAL_RL = [0, 18, 28, 46, 54, 56];
33
41
  export const CENTER2_CT_STATES = 6435;
@@ -112,6 +120,11 @@ const ALIGNMENT_CANDIDATES = [
112
120
  ["Uw", "Dw'"]
113
121
  ];
114
122
  function center2InvariantDiffs(centers) {
123
+ const rlAxis = centers[RL_POS[7]] % 3;
124
+ for (let i = 0; i < 7; i++) {
125
+ if (centers[RL_POS[i]] % 3 !== rlAxis)
126
+ return { ctDiff: -1, rlDiff: -1 };
127
+ }
115
128
  let ctDiff = 0;
116
129
  let rlDiff = 0;
117
130
  for (let i = 0; i < 15; i++) {
@@ -124,12 +137,18 @@ function center2InvariantDiffs(centers) {
124
137
  }
125
138
  return { ctDiff, rlDiff };
126
139
  }
140
+ export const ALIGNMENT_ROTATIONS = [
141
+ [],
142
+ ["z"],
143
+ ["y"]
144
+ ];
127
145
  /**
128
146
  * Phase-1 → phase-2 bridge: apply the shortest alignment candidate that
129
147
  * makes the state coordinate-invariant, then encode.
130
148
  */
131
149
  export function center2Bridge(state) {
132
- for (const alignment of ALIGNMENT_CANDIDATES) {
150
+ for (let idx = 0; idx < ALIGNMENT_CANDIDATES.length; idx++) {
151
+ const alignment = ALIGNMENT_CANDIDATES[idx];
133
152
  const aligned = alignment.length === 0
134
153
  ? state
135
154
  : applyCube4Algorithm(state, alignment);
@@ -137,6 +156,7 @@ export function center2Bridge(state) {
137
156
  if (ctDiff === 8 && rlDiff === 4) {
138
157
  return {
139
158
  alignment: [...alignment],
159
+ rotation: [...ALIGNMENT_ROTATIONS[idx]],
140
160
  state: aligned,
141
161
  encoded: encodeCenter2(aligned)
142
162
  };
@@ -246,7 +266,31 @@ export function generateCenter2Tables() {
246
266
  }
247
267
  }
248
268
  }
249
- return { ctMove, rlMove, pruning };
269
+ // Wide pruning BFS: same goals, all 28 moves (exact distances for the
270
+ // wide search; the 23-move table overestimates there).
271
+ const pruningWide = new Uint8Array(CENTER2_STATES).fill(0xff);
272
+ const wideQueue = [];
273
+ for (const rl of CENTER2_GOAL_RL) {
274
+ pruningWide[rl] = 0;
275
+ wideQueue.push(rl);
276
+ }
277
+ let wideHead = 0;
278
+ while (wideHead < wideQueue.length) {
279
+ const idx = wideQueue[wideHead++];
280
+ const ct = Math.floor(idx / CENTER2_RL_STATES);
281
+ const rl = idx % CENTER2_RL_STATES;
282
+ const d = pruningWide[idx];
283
+ for (let m = 0; m < CENTER2_SEARCH_MOVES_WIDE.length; m++) {
284
+ const nextCt = ctMove[ct * CENTER2_MOVES.length + m];
285
+ const nextRl = rlMove[rl * CENTER2_MOVES.length + m];
286
+ const next = nextCt * CENTER2_RL_STATES + nextRl;
287
+ if (pruningWide[next] === 0xff) {
288
+ pruningWide[next] = d + 1;
289
+ wideQueue.push(next);
290
+ }
291
+ }
292
+ }
293
+ return { ctMove, rlMove, pruning, pruningWide };
250
294
  }
251
295
  // The reference's cancellation rule (ckmv over move2std): skip the same base
252
296
  // move, or the same axis-class when the previous move's standard index is
@@ -320,7 +364,7 @@ export function solveCenter2All(startCt, startRl, tables, maxLength = 40, cap =
320
364
  }
321
365
  }
322
366
  let cachedWingMaps = null;
323
- function getWingMoveMaps() {
367
+ export function getWingMoveMaps() {
324
368
  if (!cachedWingMaps) {
325
369
  cachedWingMaps = buildWingMoveMaps();
326
370
  }
@@ -421,24 +465,24 @@ function buildWingMoveMaps() {
421
465
  // ---------------------------------------------------------------------------
422
466
  // Verification gates
423
467
  // ---------------------------------------------------------------------------
424
- export function verifyCenter2(tables) {
468
+ export function verifyCenter2(tables, pruning = tables.pruning, moves = CENTER2_SEARCH_MOVES) {
425
469
  const errors = [];
426
470
  for (const rl of CENTER2_GOAL_RL) {
427
- if (tables.pruning[rl] !== 0) {
471
+ if (pruning[rl] !== 0) {
428
472
  errors.push(`Goal rl ${rl} must have pruning 0.`);
429
473
  }
430
474
  }
431
475
  let maxDepth = 0;
432
476
  for (let idx = 0; idx < CENTER2_STATES; idx++) {
433
- const d = tables.pruning[idx];
477
+ const d = pruning[idx];
434
478
  if (d > maxDepth)
435
479
  maxDepth = d;
436
480
  const ct = Math.floor(idx / CENTER2_RL_STATES);
437
481
  const rl = idx % CENTER2_RL_STATES;
438
- for (let m = 0; m < CENTER2_SEARCH_MOVES.length; m++) {
482
+ for (let m = 0; m < moves.length; m++) {
439
483
  const nc = tables.ctMove[ct * CENTER2_MOVES.length + m];
440
484
  const nr = tables.rlMove[rl * CENTER2_MOVES.length + m];
441
- const nd = tables.pruning[nc * CENTER2_RL_STATES + nr];
485
+ const nd = pruning[nc * CENTER2_RL_STATES + nr];
442
486
  if (nd < d - 1 || nd > d + 1) {
443
487
  errors.push(`Pruning inconsistency at ${idx} move ${CENTER2_MOVES[m]}: ${d} -> ${nd}.`);
444
488
  break;
@@ -80,6 +80,10 @@ export interface Edge3Tables {
80
80
  * `maxDepthCap` stops the BFS early (validation only).
81
81
  */
82
82
  export declare function generateEdge3Tables(maxDepthCap?: number): Edge3Tables;
83
+ /**
84
+ * Unpack 4-bit nibble-packed distances into a byte-expanded Uint8Array (1 byte per state).
85
+ */
86
+ export declare function unpackEdge3Distances(packed: Uint8Array): Uint8Array;
83
87
  /**
84
88
  * The distance lookup over a loaded distances buffer: builds the sym
85
89
  * machinery once (cached) and returns a closure from the std-form edge
@@ -1 +1 @@
1
- {"version":3,"file":"edge3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/edge3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,mEAAmE;AACnE,eAAO,MAAM,WAAW,WAAa,CAAC;AACtC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,WAAW,QAAS,CAAC;AAGlC,qEAAqE;AACrE,eAAO,MAAM,eAAe,KAAK,CAAC;AAUlC,+EAA+E;AAC/E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAEtC,CAAC;AAaF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAkCD;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CA2G3E;AAkCD,iFAAiF;AACjF,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AAMD;;;;GAIG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAS3E;AAED;;;GAGG;AACH,iBAAS,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAOD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,WAAW,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,WAAW,CAAC;IACtB,qFAAqF;IACrF,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED,wBAAgB,cAAc,IAAI,SAAS,CAoE1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA6BR;AAMD,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAK,GAAG,WAAW,CAkEjE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,UAAU,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CASvF;AAMD,uBAAuB;AACvB,wBAAgB,gBAAgB;;;;;;;EAE/B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAgBzD"}
1
+ {"version":3,"file":"edge3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/edge3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,mEAAmE;AACnE,eAAO,MAAM,WAAW,WAAa,CAAC;AACtC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,WAAW,QAAS,CAAC;AAGlC,qEAAqE;AACrE,eAAO,MAAM,eAAe,KAAK,CAAC;AAUlC,+EAA+E;AAC/E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAEtC,CAAC;AAaF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAkCD;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CA2G3E;AAkCD,iFAAiF;AACjF,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AAMD;;;;GAIG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAS3E;AAED;;;GAGG;AACH,iBAAS,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAOD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,WAAW,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,WAAW,CAAC;IACtB,qFAAqF;IACrF,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED,wBAAgB,cAAc,IAAI,SAAS,CAoE1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA6BR;AAMD,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAK,GAAG,WAAW,CAmEjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CASnE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,UAAU,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAWvF;AAMD,uBAAuB;AACvB,wBAAgB,gBAAgB;;;;;;;EAE/B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAkBzD"}
@@ -468,8 +468,23 @@ export function generateEdge3Tables(maxDepthCap = -1) {
468
468
  frontier = next;
469
469
  depth++;
470
470
  }
471
- const distanceOfEdge = edge3DistanceLookup(distances);
472
- return { distances, distanceOfEdge };
471
+ const unpacked = unpackEdge3Distances(distances);
472
+ const distanceOfEdge = edge3DistanceLookup(unpacked);
473
+ return { distances: unpacked, distanceOfEdge };
474
+ }
475
+ /**
476
+ * Unpack 4-bit nibble-packed distances into a byte-expanded Uint8Array (1 byte per state).
477
+ */
478
+ export function unpackEdge3Distances(packed) {
479
+ if (packed.length >= EDGE3_TOTAL)
480
+ return packed;
481
+ const unpacked = new Uint8Array(EDGE3_TOTAL);
482
+ for (let i = 0; i < packed.length; i++) {
483
+ const b = packed[i];
484
+ unpacked[i << 1] = b & 0x0f;
485
+ unpacked[(i << 1) | 1] = b >> 4;
486
+ }
487
+ return unpacked;
473
488
  }
474
489
  /**
475
490
  * The distance lookup over a loaded distances buffer: builds the sym
@@ -480,9 +495,12 @@ export function edge3DistanceLookup(distances) {
480
495
  const t = symTables ?? buildSymTables();
481
496
  const { sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto } = t;
482
497
  const identity = Uint8Array.from({ length: 12 }, (_, i) => i);
498
+ const isUnpacked = distances.length >= EDGE3_TOTAL;
483
499
  return (e) => {
484
500
  const std = normalizePairingState(e, identity).edge;
485
501
  const idx = getMvSymFrom(std, identity, 20, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto) >> 3;
502
+ if (isUnpacked)
503
+ return distances[idx];
486
504
  return (distances[idx >> 1] >> ((idx & 1) * 4)) & 0x0f;
487
505
  };
488
506
  }
@@ -500,8 +518,10 @@ export function verifyEdge3(tables) {
500
518
  errors.push("Solved state must have distance 0.");
501
519
  }
502
520
  let seen = 0;
521
+ const isUnpacked = tables.distances.length >= EDGE3_TOTAL;
503
522
  for (let i = 0; i < EDGE3_TOTAL; i++) {
504
- if (((tables.distances[i >> 1] >> ((i & 1) * 4)) & 0x0f) !== 0x0f)
523
+ const d = isUnpacked ? tables.distances[i] : ((tables.distances[i >> 1] >> ((i & 1) * 4)) & 0x0f);
524
+ if (d !== 0x0f)
505
525
  seen++;
506
526
  }
507
527
  if (seen !== EDGE3_TOTAL) {