diff --git a/web/src/cases/pll-arrows.test.ts b/web/src/cases/pll-arrows.test.ts index ac3525e..48f5df2 100644 --- a/web/src/cases/pll-arrows.test.ts +++ b/web/src/cases/pll-arrows.test.ts @@ -31,6 +31,20 @@ describe('pllArrows', () => { expect(arrowsOf('T').filter((a) => a.both)).toHaveLength(2) }) + it('ignores the AUF at the end of the algorithm', () => { + // 動くパーツの数(片向きの矢印は1本で1つ、入れ替えは1本で2つ) + const moved = (name: string) => arrowsOf(name).reduce((n, a) => n + (a.both ? 2 : 1), 0) + const expected: Record = { + Aa: 3, Ab: 3, E: 4, H: 4, Ua: 3, Ub: 3, Z: 4, F: 4, Ja: 4, Jb: 4, Ra: 4, Rb: 4, T: 4, + Ga: 6, Gb: 6, Gc: 6, Gd: 6, Na: 4, Nb: 4, V: 4, Y: 4, + } + for (const [name, n] of Object.entries(expected)) expect(moved(name), name).toBe(n) + // J・R・T・F・V・Y・N は、角の入れ替えとエッジの入れ替えが1組ずつ + for (const name of ['Ja', 'Jb', 'Ra', 'Rb', 'T', 'F', 'V', 'Y', 'Na', 'Nb', 'Z']) { + expect(arrowsOf(name).every((a) => a.both), name).toBe(true) + } + }) + it('gives arrows for every PLL case, and none for a solved or non-PLL state', () => { for (const c of ALL_CASES.filter((x) => x.set === 'PLL')) { expect(pllArrows(casePattern(kpuzzle, c.alg)).length, c.name).toBeGreaterThan(0) diff --git a/web/src/cases/pll-arrows.ts b/web/src/cases/pll-arrows.ts index 911d694..5245d28 100644 --- a/web/src/cases/pll-arrows.ts +++ b/web/src/cases/pll-arrows.ts @@ -10,18 +10,30 @@ const TOP = [...CORNER_SLOTS.slice(0, 4), ...EDGE_SLOTS.slice(0, 4)] const sideKey = (faces: string[]) => [...faces].sort().join('') +// 解き終わりの AUF(U)は、側面の色の名前を巡回させるのと同じ +const NEXT: Record = { F: 'R', R: 'B', B: 'L', L: 'F' } +const cycle = (face: string, k: number) => { + for (let i = 0; i < k; i++) face = NEXT[face] ?? face + return face +} + /** * 各パーツを、今ある位置から戻るべき位置への矢印にする。2つのパーツの入れ替えは両向きの矢印1本にまとめる。 - * 最終層の向きが揃っていない(PLL の状態でない)ときは空。 + * 解き終わりの AUF は自由なので、動かすパーツが最も少なくなる AUF で考える(ケースの手順が AUF を含んでいても、 + * 最終層全体が回っているようには描かない)。最終層の向きが揃っていない(PLL の状態でない)ときは空。 */ export function pllArrows(pattern: string): PllArrow[] { if (TOP.some((s) => pattern[s.facelets[0]] !== 'U')) return [] // 側面の色の組 → その色のパーツが戻るべき位置 const home = new Map(TOP.map((s) => [sideKey(s.facelets.slice(1).map((i) => SOLVED_FACELETS[i])), s.facelets[0]])) - const dest = new Map() - for (const s of TOP) { - const to = home.get(sideKey(s.facelets.slice(1).map((i) => pattern[i]))) - if (to !== undefined && to !== s.facelets[0]) dest.set(s.facelets[0], to) + let dest = new Map() + for (let k = 0; k < 4; k++) { + const d = new Map() + for (const s of TOP) { + const to = home.get(sideKey(s.facelets.slice(1).map((i) => cycle(pattern[i], k)))) + if (to !== undefined && to !== s.facelets[0]) d.set(s.facelets[0], to) + } + if (k === 0 || d.size < dest.size) dest = d } const out: PllArrow[] = [] for (const [from, to] of dest) {