cube-state-engine 1.6.0 → 1.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.
- package/.claude/settings.local.json +6 -1
- package/dist/index.d.mts +42 -21
- package/dist/index.d.ts +42 -21
- package/dist/index.js +22 -15
- package/dist/index.mjs +22 -15
- package/package.json +1 -1
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(npm run *)",
|
|
5
5
|
"Bash(node -e \"const m=require\\('./dist/index.js'\\); console.log\\(Object.keys\\(m\\).filter\\(k=>/analyze|invert|MovePerm/.test\\(k\\)\\)\\)\")",
|
|
6
|
-
"Bash(npm test *)"
|
|
6
|
+
"Bash(npm test *)",
|
|
7
|
+
"Read(//mnt/d/workspace/**)",
|
|
8
|
+
"Bash(node -e ' *)",
|
|
9
|
+
"Bash(node *)",
|
|
10
|
+
"Bash(cd /tmp)",
|
|
11
|
+
"Bash(sed -i 's/console.log\\(`\\\\\\\\nsideA.*cmll\\)}`\\);/console.log\\(\"\\\\\\\\nsideA=\"+sideA+\" up=\"+up+\": aFirst=\"+firstTrue\\(aDone\\)+\" aLast=\"+aDone[n-1]+\" bFirst=\"+firstTrue\\(bDone\\)+\" bLast=\"+bDone[n-1]+\" cmllFirst=\"+firstTrue\\(cmll\\)\\);/' trace.mjs)"
|
|
7
12
|
]
|
|
8
13
|
}
|
|
9
14
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -328,14 +328,27 @@ function rouxBlockPieces(geo, sideFace, upFace) {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
// A Roux block is done when its three edges and two corners are all placed.
|
|
331
|
-
|
|
331
|
+
// As with CMLL, the block stays intact while the slice between the two blocks is
|
|
332
|
+
// unaligned, but the block's down/front/back facelets are compared against the
|
|
333
|
+
// floating centers -- which the slice displaces. So we accept the block if ANY
|
|
334
|
+
// of the four slice rotations makes its pieces match the centers: the slice
|
|
335
|
+
// moves neither the blocks nor their corners/edges (none sit on the slice), it
|
|
336
|
+
// only re-aligns the floating centers. Without this, a perfectly built block
|
|
337
|
+
// reads as broken for most of a Roux solve, because the slice is scrambled until
|
|
338
|
+
// LSE. `slicePerm` is the slice perpendicular to the block axis.
|
|
339
|
+
function rouxBlockDone(st, geo, sideFace, upFace, slicePerm) {
|
|
332
340
|
const { edges, corners } = rouxBlockPieces(geo, sideFace, upFace);
|
|
333
341
|
if (edges.length !== 3 || corners.length !== 2) return false;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
342
|
+
let cur = st;
|
|
343
|
+
for (let m = 0; m < 4; m++) {
|
|
344
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
345
|
+
const centers = centersOf(cur, geo);
|
|
346
|
+
const ok =
|
|
347
|
+
edges.every((e) => slotCorrect(cur, centers, e.indices, geo.per)) &&
|
|
348
|
+
corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per));
|
|
349
|
+
if (ok) return true;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
339
352
|
}
|
|
340
353
|
|
|
341
354
|
// Applies a permutation (out[i] = st[perm[i]]) to a flat sticker array.
|
|
@@ -346,15 +359,17 @@ function applyPerm(st, perm) {
|
|
|
346
359
|
}
|
|
347
360
|
|
|
348
361
|
// CMLL is done when the four corners touching the up face are solved, allowing
|
|
349
|
-
// the
|
|
350
|
-
// rotations makes those corners correct.
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
|
|
362
|
+
// the slice between the two blocks to be unaligned: we accept the state if ANY
|
|
363
|
+
// of the four slice rotations makes those corners correct. The slice moves
|
|
364
|
+
// neither the corners nor the two blocks, only the four floating centers (and
|
|
365
|
+
// slice edges) around the block axis, so this captures exactly "last-layer
|
|
366
|
+
// corners solved, slice not necessarily aligned yet". `slicePerm` must be the
|
|
367
|
+
// slice perpendicular to the block axis (M for L/R blocks, E for U/D, S for F/B).
|
|
368
|
+
function cmllDone(st, geo, upFace, slicePerm) {
|
|
354
369
|
let cur = st;
|
|
355
370
|
const corners = geo.cornersByFace(upFace);
|
|
356
371
|
for (let m = 0; m < 4; m++) {
|
|
357
|
-
if (m > 0) cur = applyPerm(cur,
|
|
372
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
358
373
|
const centers = centersOf(cur, geo);
|
|
359
374
|
if (corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per)))
|
|
360
375
|
return true;
|
|
@@ -434,16 +449,15 @@ function isCFOP(build) {
|
|
|
434
449
|
// while the first block holds, CMLL only once both blocks hold, and LSE is the
|
|
435
450
|
// fully solved cube. The first block is whichever of the two opposite side faces
|
|
436
451
|
// finishes its block earliest; the other side is the second block.
|
|
437
|
-
function buildForRoux(snapshots, geo, sideA, upFace,
|
|
452
|
+
function buildForRoux(snapshots, geo, sideA, upFace, slicePerm) {
|
|
438
453
|
snapshots.length;
|
|
439
454
|
const sideB = geo.opposite[sideA];
|
|
440
455
|
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
rouxBlockDone(st, centersAt[i], geo, sideA, upFace)
|
|
456
|
+
const aDone = snapshots.map((st) =>
|
|
457
|
+
rouxBlockDone(st, geo, sideA, upFace, slicePerm)
|
|
444
458
|
);
|
|
445
|
-
const bDone = snapshots.map((st
|
|
446
|
-
rouxBlockDone(st,
|
|
459
|
+
const bDone = snapshots.map((st) =>
|
|
460
|
+
rouxBlockDone(st, geo, sideB, upFace, slicePerm)
|
|
447
461
|
);
|
|
448
462
|
const aIdx = completionIndex(aDone);
|
|
449
463
|
const bIdx = completionIndex(bDone);
|
|
@@ -467,7 +481,7 @@ function buildForRoux(snapshots, geo, sideA, upFace, mPerm) {
|
|
|
467
481
|
|
|
468
482
|
const cmllIdx = completionIndex(
|
|
469
483
|
snapshots.map(
|
|
470
|
-
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace,
|
|
484
|
+
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace, slicePerm)
|
|
471
485
|
)
|
|
472
486
|
);
|
|
473
487
|
const lseIdx = completionIndex(snapshots.map((st) => isSolvedFlat(st, geo.per)));
|
|
@@ -611,12 +625,19 @@ function analyzeSolution(moves, options = {}) {
|
|
|
611
625
|
// Stage the solve as Roux (1st block -> 2nd block -> CMLL -> LSE) on every
|
|
612
626
|
// orientation. Each candidate fixes a side face and a perpendicular up face;
|
|
613
627
|
// buildForRoux assigns first/second block by which side finishes earliest.
|
|
628
|
+
// The "floating" slice that block/CMLL detection lets drift is the one
|
|
629
|
+
// perpendicular to the block axis: M for L/R blocks, E for U/D, S for F/B.
|
|
614
630
|
// Pick the valid candidate whose first block completes earliest.
|
|
615
|
-
const
|
|
631
|
+
const perms = getMovePermutations(size);
|
|
632
|
+
const axisSlicePerm = (face) => {
|
|
633
|
+
if (face === 1 || face === 3) return perms["M"].cw; // L/R axis
|
|
634
|
+
if (face === 0 || face === 5) return perms["E"].cw; // U/D axis
|
|
635
|
+
return perms["S"].cw; // F/B axis
|
|
636
|
+
};
|
|
616
637
|
const rouxCandidates = [];
|
|
617
638
|
for (let s = 0; s < 6; s++) {
|
|
618
639
|
for (const u of geo.neighbors[s]) {
|
|
619
|
-
rouxCandidates.push(buildForRoux(snapshots, geo, s, u,
|
|
640
|
+
rouxCandidates.push(buildForRoux(snapshots, geo, s, u, axisSlicePerm(s)));
|
|
620
641
|
}
|
|
621
642
|
}
|
|
622
643
|
const rouxBuild =
|
package/dist/index.d.ts
CHANGED
|
@@ -328,14 +328,27 @@ function rouxBlockPieces(geo, sideFace, upFace) {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
// A Roux block is done when its three edges and two corners are all placed.
|
|
331
|
-
|
|
331
|
+
// As with CMLL, the block stays intact while the slice between the two blocks is
|
|
332
|
+
// unaligned, but the block's down/front/back facelets are compared against the
|
|
333
|
+
// floating centers -- which the slice displaces. So we accept the block if ANY
|
|
334
|
+
// of the four slice rotations makes its pieces match the centers: the slice
|
|
335
|
+
// moves neither the blocks nor their corners/edges (none sit on the slice), it
|
|
336
|
+
// only re-aligns the floating centers. Without this, a perfectly built block
|
|
337
|
+
// reads as broken for most of a Roux solve, because the slice is scrambled until
|
|
338
|
+
// LSE. `slicePerm` is the slice perpendicular to the block axis.
|
|
339
|
+
function rouxBlockDone(st, geo, sideFace, upFace, slicePerm) {
|
|
332
340
|
const { edges, corners } = rouxBlockPieces(geo, sideFace, upFace);
|
|
333
341
|
if (edges.length !== 3 || corners.length !== 2) return false;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
342
|
+
let cur = st;
|
|
343
|
+
for (let m = 0; m < 4; m++) {
|
|
344
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
345
|
+
const centers = centersOf(cur, geo);
|
|
346
|
+
const ok =
|
|
347
|
+
edges.every((e) => slotCorrect(cur, centers, e.indices, geo.per)) &&
|
|
348
|
+
corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per));
|
|
349
|
+
if (ok) return true;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
339
352
|
}
|
|
340
353
|
|
|
341
354
|
// Applies a permutation (out[i] = st[perm[i]]) to a flat sticker array.
|
|
@@ -346,15 +359,17 @@ function applyPerm(st, perm) {
|
|
|
346
359
|
}
|
|
347
360
|
|
|
348
361
|
// CMLL is done when the four corners touching the up face are solved, allowing
|
|
349
|
-
// the
|
|
350
|
-
// rotations makes those corners correct.
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
|
|
362
|
+
// the slice between the two blocks to be unaligned: we accept the state if ANY
|
|
363
|
+
// of the four slice rotations makes those corners correct. The slice moves
|
|
364
|
+
// neither the corners nor the two blocks, only the four floating centers (and
|
|
365
|
+
// slice edges) around the block axis, so this captures exactly "last-layer
|
|
366
|
+
// corners solved, slice not necessarily aligned yet". `slicePerm` must be the
|
|
367
|
+
// slice perpendicular to the block axis (M for L/R blocks, E for U/D, S for F/B).
|
|
368
|
+
function cmllDone(st, geo, upFace, slicePerm) {
|
|
354
369
|
let cur = st;
|
|
355
370
|
const corners = geo.cornersByFace(upFace);
|
|
356
371
|
for (let m = 0; m < 4; m++) {
|
|
357
|
-
if (m > 0) cur = applyPerm(cur,
|
|
372
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
358
373
|
const centers = centersOf(cur, geo);
|
|
359
374
|
if (corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per)))
|
|
360
375
|
return true;
|
|
@@ -434,16 +449,15 @@ function isCFOP(build) {
|
|
|
434
449
|
// while the first block holds, CMLL only once both blocks hold, and LSE is the
|
|
435
450
|
// fully solved cube. The first block is whichever of the two opposite side faces
|
|
436
451
|
// finishes its block earliest; the other side is the second block.
|
|
437
|
-
function buildForRoux(snapshots, geo, sideA, upFace,
|
|
452
|
+
function buildForRoux(snapshots, geo, sideA, upFace, slicePerm) {
|
|
438
453
|
snapshots.length;
|
|
439
454
|
const sideB = geo.opposite[sideA];
|
|
440
455
|
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
rouxBlockDone(st, centersAt[i], geo, sideA, upFace)
|
|
456
|
+
const aDone = snapshots.map((st) =>
|
|
457
|
+
rouxBlockDone(st, geo, sideA, upFace, slicePerm)
|
|
444
458
|
);
|
|
445
|
-
const bDone = snapshots.map((st
|
|
446
|
-
rouxBlockDone(st,
|
|
459
|
+
const bDone = snapshots.map((st) =>
|
|
460
|
+
rouxBlockDone(st, geo, sideB, upFace, slicePerm)
|
|
447
461
|
);
|
|
448
462
|
const aIdx = completionIndex(aDone);
|
|
449
463
|
const bIdx = completionIndex(bDone);
|
|
@@ -467,7 +481,7 @@ function buildForRoux(snapshots, geo, sideA, upFace, mPerm) {
|
|
|
467
481
|
|
|
468
482
|
const cmllIdx = completionIndex(
|
|
469
483
|
snapshots.map(
|
|
470
|
-
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace,
|
|
484
|
+
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace, slicePerm)
|
|
471
485
|
)
|
|
472
486
|
);
|
|
473
487
|
const lseIdx = completionIndex(snapshots.map((st) => isSolvedFlat(st, geo.per)));
|
|
@@ -611,12 +625,19 @@ function analyzeSolution(moves, options = {}) {
|
|
|
611
625
|
// Stage the solve as Roux (1st block -> 2nd block -> CMLL -> LSE) on every
|
|
612
626
|
// orientation. Each candidate fixes a side face and a perpendicular up face;
|
|
613
627
|
// buildForRoux assigns first/second block by which side finishes earliest.
|
|
628
|
+
// The "floating" slice that block/CMLL detection lets drift is the one
|
|
629
|
+
// perpendicular to the block axis: M for L/R blocks, E for U/D, S for F/B.
|
|
614
630
|
// Pick the valid candidate whose first block completes earliest.
|
|
615
|
-
const
|
|
631
|
+
const perms = getMovePermutations(size);
|
|
632
|
+
const axisSlicePerm = (face) => {
|
|
633
|
+
if (face === 1 || face === 3) return perms["M"].cw; // L/R axis
|
|
634
|
+
if (face === 0 || face === 5) return perms["E"].cw; // U/D axis
|
|
635
|
+
return perms["S"].cw; // F/B axis
|
|
636
|
+
};
|
|
616
637
|
const rouxCandidates = [];
|
|
617
638
|
for (let s = 0; s < 6; s++) {
|
|
618
639
|
for (const u of geo.neighbors[s]) {
|
|
619
|
-
rouxCandidates.push(buildForRoux(snapshots, geo, s, u,
|
|
640
|
+
rouxCandidates.push(buildForRoux(snapshots, geo, s, u, axisSlicePerm(s)));
|
|
620
641
|
}
|
|
621
642
|
}
|
|
622
643
|
const rouxBuild =
|
package/dist/index.js
CHANGED
|
@@ -276,25 +276,28 @@ function rouxBlockPieces(geo, sideFace, upFace) {
|
|
|
276
276
|
const corners = geo.cornersByFace(sideFace).filter((c) => c.faces.includes(downFace));
|
|
277
277
|
return { edges, corners };
|
|
278
278
|
}
|
|
279
|
-
function rouxBlockDone(st,
|
|
279
|
+
function rouxBlockDone(st, geo, sideFace, upFace, slicePerm) {
|
|
280
280
|
const { edges, corners } = rouxBlockPieces(geo, sideFace, upFace);
|
|
281
281
|
if (edges.length !== 3 || corners.length !== 2) return false;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
let cur = st;
|
|
283
|
+
for (let m = 0; m < 4; m++) {
|
|
284
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
285
|
+
const centers = centersOf(cur, geo);
|
|
286
|
+
const ok = edges.every((e) => slotCorrect(cur, centers, e.indices, geo.per)) && corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per));
|
|
287
|
+
if (ok) return true;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
287
290
|
}
|
|
288
291
|
function applyPerm(st, perm) {
|
|
289
292
|
const out = new Array(st.length);
|
|
290
293
|
for (let i = 0; i < st.length; i++) out[i] = st[perm[i]];
|
|
291
294
|
return out;
|
|
292
295
|
}
|
|
293
|
-
function cmllDone(st, geo, upFace,
|
|
296
|
+
function cmllDone(st, geo, upFace, slicePerm) {
|
|
294
297
|
let cur = st;
|
|
295
298
|
const corners = geo.cornersByFace(upFace);
|
|
296
299
|
for (let m = 0; m < 4; m++) {
|
|
297
|
-
if (m > 0) cur = applyPerm(cur,
|
|
300
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
298
301
|
const centers = centersOf(cur, geo);
|
|
299
302
|
if (corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per)))
|
|
300
303
|
return true;
|
|
@@ -346,15 +349,14 @@ function isCFOP(build) {
|
|
|
346
349
|
const lastF2L = f2lSlots[3].idx;
|
|
347
350
|
return ollIdx >= lastF2L && pllIdx >= ollIdx;
|
|
348
351
|
}
|
|
349
|
-
function buildForRoux(snapshots, geo, sideA, upFace,
|
|
352
|
+
function buildForRoux(snapshots, geo, sideA, upFace, slicePerm) {
|
|
350
353
|
const n = snapshots.length;
|
|
351
354
|
const sideB = geo.opposite[sideA];
|
|
352
|
-
const centersAt = snapshots.map((st) => centersOf(st, geo));
|
|
353
355
|
const aDone = snapshots.map(
|
|
354
|
-
(st
|
|
356
|
+
(st) => rouxBlockDone(st, geo, sideA, upFace, slicePerm)
|
|
355
357
|
);
|
|
356
358
|
const bDone = snapshots.map(
|
|
357
|
-
(st
|
|
359
|
+
(st) => rouxBlockDone(st, geo, sideB, upFace, slicePerm)
|
|
358
360
|
);
|
|
359
361
|
const aIdx = completionIndex(aDone);
|
|
360
362
|
const bIdx = completionIndex(bDone);
|
|
@@ -373,7 +375,7 @@ function buildForRoux(snapshots, geo, sideA, upFace, mPerm) {
|
|
|
373
375
|
const sbIdx = completionIndex(secondBlockBools);
|
|
374
376
|
const cmllIdx = completionIndex(
|
|
375
377
|
snapshots.map(
|
|
376
|
-
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace,
|
|
378
|
+
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace, slicePerm)
|
|
377
379
|
)
|
|
378
380
|
);
|
|
379
381
|
const lseIdx = completionIndex(snapshots.map((st) => isSolvedFlat(st, geo.per)));
|
|
@@ -469,11 +471,16 @@ function analyzeSolution(moves, options = {}) {
|
|
|
469
471
|
});
|
|
470
472
|
const cfopChosen = (_a = ordered.find((c) => isCFOP(c.build))) != null ? _a : ordered[0];
|
|
471
473
|
const cfopValid = !!cfopChosen && isCFOP(cfopChosen.build);
|
|
472
|
-
const
|
|
474
|
+
const perms = getMovePermutations(size);
|
|
475
|
+
const axisSlicePerm = (face) => {
|
|
476
|
+
if (face === 1 || face === 3) return perms["M"].cw;
|
|
477
|
+
if (face === 0 || face === 5) return perms["E"].cw;
|
|
478
|
+
return perms["S"].cw;
|
|
479
|
+
};
|
|
473
480
|
const rouxCandidates = [];
|
|
474
481
|
for (let s = 0; s < 6; s++) {
|
|
475
482
|
for (const u of geo.neighbors[s]) {
|
|
476
|
-
rouxCandidates.push(buildForRoux(snapshots, geo, s, u,
|
|
483
|
+
rouxCandidates.push(buildForRoux(snapshots, geo, s, u, axisSlicePerm(s)));
|
|
477
484
|
}
|
|
478
485
|
}
|
|
479
486
|
const rouxBuild = (_b = rouxCandidates.filter((b) => isRoux(b)).sort((a, b) => a.fbIdx - b.fbIdx)[0]) != null ? _b : null;
|
package/dist/index.mjs
CHANGED
|
@@ -248,25 +248,28 @@ function rouxBlockPieces(geo, sideFace, upFace) {
|
|
|
248
248
|
const corners = geo.cornersByFace(sideFace).filter((c) => c.faces.includes(downFace));
|
|
249
249
|
return { edges, corners };
|
|
250
250
|
}
|
|
251
|
-
function rouxBlockDone(st,
|
|
251
|
+
function rouxBlockDone(st, geo, sideFace, upFace, slicePerm) {
|
|
252
252
|
const { edges, corners } = rouxBlockPieces(geo, sideFace, upFace);
|
|
253
253
|
if (edges.length !== 3 || corners.length !== 2) return false;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
254
|
+
let cur = st;
|
|
255
|
+
for (let m = 0; m < 4; m++) {
|
|
256
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
257
|
+
const centers = centersOf(cur, geo);
|
|
258
|
+
const ok = edges.every((e) => slotCorrect(cur, centers, e.indices, geo.per)) && corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per));
|
|
259
|
+
if (ok) return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
259
262
|
}
|
|
260
263
|
function applyPerm(st, perm) {
|
|
261
264
|
const out = new Array(st.length);
|
|
262
265
|
for (let i = 0; i < st.length; i++) out[i] = st[perm[i]];
|
|
263
266
|
return out;
|
|
264
267
|
}
|
|
265
|
-
function cmllDone(st, geo, upFace,
|
|
268
|
+
function cmllDone(st, geo, upFace, slicePerm) {
|
|
266
269
|
let cur = st;
|
|
267
270
|
const corners = geo.cornersByFace(upFace);
|
|
268
271
|
for (let m = 0; m < 4; m++) {
|
|
269
|
-
if (m > 0) cur = applyPerm(cur,
|
|
272
|
+
if (m > 0) cur = applyPerm(cur, slicePerm);
|
|
270
273
|
const centers = centersOf(cur, geo);
|
|
271
274
|
if (corners.every((c) => slotCorrect(cur, centers, c.indices, geo.per)))
|
|
272
275
|
return true;
|
|
@@ -318,15 +321,14 @@ function isCFOP(build) {
|
|
|
318
321
|
const lastF2L = f2lSlots[3].idx;
|
|
319
322
|
return ollIdx >= lastF2L && pllIdx >= ollIdx;
|
|
320
323
|
}
|
|
321
|
-
function buildForRoux(snapshots, geo, sideA, upFace,
|
|
324
|
+
function buildForRoux(snapshots, geo, sideA, upFace, slicePerm) {
|
|
322
325
|
const n = snapshots.length;
|
|
323
326
|
const sideB = geo.opposite[sideA];
|
|
324
|
-
const centersAt = snapshots.map((st) => centersOf(st, geo));
|
|
325
327
|
const aDone = snapshots.map(
|
|
326
|
-
(st
|
|
328
|
+
(st) => rouxBlockDone(st, geo, sideA, upFace, slicePerm)
|
|
327
329
|
);
|
|
328
330
|
const bDone = snapshots.map(
|
|
329
|
-
(st
|
|
331
|
+
(st) => rouxBlockDone(st, geo, sideB, upFace, slicePerm)
|
|
330
332
|
);
|
|
331
333
|
const aIdx = completionIndex(aDone);
|
|
332
334
|
const bIdx = completionIndex(bDone);
|
|
@@ -345,7 +347,7 @@ function buildForRoux(snapshots, geo, sideA, upFace, mPerm) {
|
|
|
345
347
|
const sbIdx = completionIndex(secondBlockBools);
|
|
346
348
|
const cmllIdx = completionIndex(
|
|
347
349
|
snapshots.map(
|
|
348
|
-
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace,
|
|
350
|
+
(st, i) => secondBlockBools[i] && cmllDone(st, geo, upFace, slicePerm)
|
|
349
351
|
)
|
|
350
352
|
);
|
|
351
353
|
const lseIdx = completionIndex(snapshots.map((st) => isSolvedFlat(st, geo.per)));
|
|
@@ -441,11 +443,16 @@ function analyzeSolution(moves, options = {}) {
|
|
|
441
443
|
});
|
|
442
444
|
const cfopChosen = (_a = ordered.find((c) => isCFOP(c.build))) != null ? _a : ordered[0];
|
|
443
445
|
const cfopValid = !!cfopChosen && isCFOP(cfopChosen.build);
|
|
444
|
-
const
|
|
446
|
+
const perms = getMovePermutations(size);
|
|
447
|
+
const axisSlicePerm = (face) => {
|
|
448
|
+
if (face === 1 || face === 3) return perms["M"].cw;
|
|
449
|
+
if (face === 0 || face === 5) return perms["E"].cw;
|
|
450
|
+
return perms["S"].cw;
|
|
451
|
+
};
|
|
445
452
|
const rouxCandidates = [];
|
|
446
453
|
for (let s = 0; s < 6; s++) {
|
|
447
454
|
for (const u of geo.neighbors[s]) {
|
|
448
|
-
rouxCandidates.push(buildForRoux(snapshots, geo, s, u,
|
|
455
|
+
rouxCandidates.push(buildForRoux(snapshots, geo, s, u, axisSlicePerm(s)));
|
|
449
456
|
}
|
|
450
457
|
}
|
|
451
458
|
const rouxBuild = (_b = rouxCandidates.filter((b) => isRoux(b)).sort((a, b) => a.fbIdx - b.fbIdx)[0]) != null ? _b : null;
|
package/package.json
CHANGED