目录
一,JS部分
参考三阶魔方
二,自动复原
三,八阶魔方
1,网页版
JS部分参考上文
HTML部分:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>八阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>八阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R5'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(26, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(11.5, 8.8, 14.5);
camera.lookAt(0, -0.3, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 八阶坐标 ----------
const N = 8;
const OUT = 3.5;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-3.5 + i);
}
// 块尺寸 0.82,间隙 0.18,紧凑且视觉舒适
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块 ----------
let cubies = [];
const pivot = new THREE.Group();
scene.add(pivot);
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return { mesh, home: new THREE.Vector3(x, y, z), colors };
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 旋转引擎 ----------
const moveQueue = [];
let busy = false;
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
function snap(v) { return Math.round(v * 2) / 2; }
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return null;
const targets = def.targets;
return cubies.filter(c => {
const val = c.mesh.position[def.axis];
return targets.some(t => Math.abs(val - t) < 0.01);
});
}
function performMove(faceKey, dir, duration, done) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
performRotation(cubies.slice(), axis, -dir, duration, done);
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) { done(); return; }
const selected = selectCubiesForLayer(faceKey);
if (!selected || selected.length === 0) { done(); return; }
const rotDir = dir * def.sign;
performRotation(selected, def.axis, rotDir, duration, done);
}
function performRotation(selected, axis, dir, duration, done) {
selected.forEach(c => pivot.attach(c.mesh));
const angle = dir * Math.PI / 2;
const start = performance.now();
function frame(now) {
let t = Math.min(1, (now - start) / duration);
const e = easeInOutCubic(t);
pivot.rotation.set(0, 0, 0);
pivot.rotation[axis] = angle * e;
if (t < 1) {
requestAnimationFrame(frame);
} else {
pivot.rotation.set(0, 0, 0);
pivot.rotation[axis] = angle;
selected.forEach(c => {
scene.attach(c.mesh);
c.mesh.position.x = snap(c.mesh.position.x);
c.mesh.position.y = snap(c.mesh.position.y);
c.mesh.position.z = snap(c.mesh.position.z);
});
pivot.rotation.set(0, 0, 0);
done();
}
}
requestAnimationFrame(frame);
}
function enqueueMove(faceKey, dir, duration, opts = {}) {
moveQueue.push({
faceKey, dir, duration,
count: opts.count !== false,
record: !!opts.record,
silent: opts.silent === true,
onDone: opts.onDone
});
if (!busy) processQueue();
}
function processQueue() {
if (moveQueue.length === 0) { busy = false; return; }
busy = true;
const m = moveQueue.shift();
if (m.record) userSeq.push({ face: m.faceKey, dir: m.dir });
performMove(m.faceKey, m.dir, m.duration, () => {
if (m.count) {
moveCount++;
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved');
statusEl.classList.remove('error');
}
}
if (m.onDone) m.onDone();
if (!m.silent) updateRecordPanel();
processQueue();
});
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取 ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.mesh.position.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.mesh.quaternion.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
function seqToText(seq) {
return seq.length ? seq.map(moveNotation).join(' ') : '(无)';
}
// ---------- 棋谱 ----------
function buildRecordText() {
const lines = [];
lines.push('八阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])([2-7])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = letter + layerNum;
if (layerNum === '') faceKey = letter;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
const letter = rotMatch[1];
const dir = rotMatch[2] === "'" ? -1 : 1;
moves.push({ face: letter, dir });
continue;
}
errors.push('无法解析: ' + raw);
}
if (errors.length > 0) {
return { moves, errors };
}
return { moves, errors: [] };
}
function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
moves.forEach((mv, i) => {
const isLayer = !!LAYER_DEFS[mv.face] || !!ROT_DEF[mv.face];
const isLast = i === moves.length - 1;
enqueueMove(mv.face, mv.dir, 2, {
count: isLayer && !ROT_DEF[mv.face],
record: true,
silent: true,
onDone: isLast ? () => {
updateRecordPanel();
if (!isSolved()) {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved');
}
} : undefined
});
});
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
moveQueue.length = 0;
busy = false;
cubies.forEach(c => { if (c.mesh.parent === pivot) scene.attach(c.mesh); });
pivot.rotation.set(0, 0, 0);
cubies.forEach(c => {
c.mesh.position.copy(c.home);
c.mesh.quaternion.identity();
});
}
function generateScramble(n = 70) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(70);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
scrambleSeq.forEach((m, idx) => {
enqueueMove(m.face, m.dir, 70, {
count: false,
record: false,
onDone: idx === scrambleSeq.length - 1 ? () => {
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
updateRecordPanel();
} : undefined
});
});
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
updateRecordPanel();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const faceKey = key;
if (LAYER_DEFS[faceKey]) {
const dir = e.shiftKey ? -1 : 1;
enqueueMove(faceKey, dir, 220, { count: true, record: true });
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
enqueueMove(face, dir, 220, { count: false, record: true });
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原

完整操作序列:
D4 L7 U D6' D4' U4' F B L7 F' B6 U2' L5 R' D5 D3' U5 L F' L B5' B7' F6' U4 B4 U6' F6' L7' L2 R2 R6 B F6' R2 L5' R6' L4 F3' L5 F4 F2' L3' D7 U2 D5' R L6 D2 R5' D5' B6' U F7 B4' L4' U5' R4 F2' L5' B' D2 B7 F' L3 D6 D R6 F5 U D' x x F' U L4' U' L4 U' L4' U U L4 F x' F' U L4' U' L4 U' L4' U U L4 F z F F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F z F L4' U' L4 U' L4' U U L4 F' z z x x U L4' U' L4 U' L4' U U L4 z U U L4' U' L4 U' L4' U U L4 z z F' U' L4' U' L4 U' L4' U U L4 F z x z x' z z F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F F x x F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 x' F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F L4' U' L4 U' L4' U U L4 x F U L4' U' L4 U' L4' U U L4 F L4' U' L4 U' L4' U U L4 F F z x x F F R4 R3' U R3 R2' U' R4' R3 U R3' F F x' F' R4 R3' U R3 R2' U' R4' R3 U R3' F z F R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F' z F U U R5 R4' U R3 R2' U' R5' R4 U R3' F' z F F U' R5 R4' U R3 R2' U' R5' R4 U R3' F F z x x F' R5 R4' U R3 R2' U' R5' R4 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' z F' U R4 R3' U R3 R2' U' R4' R3 U R3' F z F' R5 R4' U R3 R2' U' R5' R4 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' z F R5 R4' U R3 R2' U' R5' R4 U R3' F F R4 R3' U R3 R2' U' R4' R3 U R3' F x z x' F' R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F z z F' U R5 R4' U R3 R2' U' R5' R4 U R3' F x x F F R5 R4' U R3 R2' U' R5' R4 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F x' F F R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' U U R4 R3' U R3 R2' U' R4' R3 U R3' F x F R5 R4' U R3 R2' U' R5' R4 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U R4 R3' U R3 R2' U' R4' R3 U R3' F z x F U L3' U' L3 U' L3' U U L3 F' z U U L3' U' L3 U' L3' U U L3 z z F' U' L3' U' L3 U' L3' U U L3 F z x x F F U L3' U' L3 U' L3' U U L3 F F z F F U U L3' U' L3 U' L3' U U L3 F F z F F U U L3' U' L3 U' L3' U U L3 F F z F F L3' U' L3 U' L3' U U L3 F F x z x' F F U L3' U' L3 U' L3' U U L3 F F z z F F U U L3' U' L3 U' L3' U U L3 F F U' L3' U' L3 U' L3' U U L3 x x F U L3' U' L3 U' L3' U U L3 F' x' F' U U L3' U' L3 U' L3' U U L3 F x F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F L3' U' L3 U' L3' U U L3 F z x x F F R4 R3' U R2 R' U' R4' R3 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F x' F F R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F z F U R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' z F F R3 R2' U R2 R' U' R3' R2 U R2' F F z F U U R4 R3' U R2 R' U' R4'
R3 U R2' F U R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F z x x F R3 R2' U R2 R' U' R3' R2 U R2' F F U R4 R3' U R2 R' U' R4' R3 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' z F F R5 R4' U R2 R' U' R5' R4 U R2' F F R3 R2' U R2 R' U' R3' R2 U R2' z F F U U R4 R3' U R2 R' U' R4' R3 U R2' F U R3 R2' U R2 R' U' R3' R2 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' z F F R6 R5' U R2 R' U' R6' R5 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' U' R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' F x z F F R3 R2' U R2 R' U' R3' R2 U R2' F U' R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' x' F R4 R3' U R2 R' U' R4' R3 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F z z F U R5 R4' U R2 R' U' R5' R4 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' F U R5 R4' U R2 R' U' R5' R4 U R2' F U' R3 R2' U R2 R' U' R3' R2 U R2' x x F U R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F x' F R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F x F U R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F F R4 R3' U R2 R' U' R4' R3 U R2' z x z F U L2' U' L2 U' L2' U U L2 F' z F U' L2' U' L2 U' L2' U U L2 F' z F F U U L2' U' L2 U' L2' U U L2 F F z x x F' U L2' U' L2 U' L2' U U L2 F z F U U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F z F F L2' U'
L2 U' L2' U U L2 F F z x z F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F x' F F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 z z F' U U L2' U' L2 U' L2' U U L2 F x x F U L2' U' L2 U' L2' U U L2 F' x' F U L2' U' L2 U' L2' U U L2 F F U' L2' U' L2 U' L2' U U L2 F x F U L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 F R2 R' U U R2 R' U U R2' R x' L2' L U U L2 L' x' U U R2 R' U U R2' R F U L L R R B U' R B B z z x' F4' F R U' R' U F4 U L U' B U B U B U D B B U' L R R B U' R B B z z x' F4' F R U' R' U F4 U L U R R B U' R B B z z x' F4' F R U' R' U F4 U L U' L R R B U' R B B z z x' F4' F R U' R' U F4 U L U' R R B U' R B B z z x' F4' F R U' R' U F4 U L L U' B U B U B U D B B R R B U' R B B z z x' F4' F R U' R' U F4 U L U' B U B U B U D B B U' L U U R R B U' R B B z z x' F4' F R U' R' U F4 U L U' B U B U B U D B B U L U R R B U' R B B z z x' F4' F R U' R' U F4 U L U' B U B U B U D B B U' L U' R R B U' R B B z z x' F4' F R U' R' U F4 F' U U F U L U' B U B U B U D B' L U B L U' z' x' F4' F R U' R' U F4 F' R' z' x' L' B D' F4' F R U' R' U F4 F' U' x x U' F' z z F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F5'
F R U' R' U F5 F' U U B L U' F U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B L L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L B L L U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' L U' B U B U B U D B B R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' L U' B U B U B U D B B U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U U R R B U' R B B z z x' F5' F
R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' B L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U B L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U B L L U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U U B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' D B D B
U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F5' F R U' R' U F5 F' U' x x U' F' z z F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L B L L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U U B L L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U U B L L U' B U B U B U D B B U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B L U' z' x' F6' F R U' R' U F6 F' R' z' x' L' B D' F6' F R U' R' U F6 F' U U B L U' F U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L B L U' z' x' F6' F R U' R' U F6 F' R' z' x' L' B D' F6' F R U' R' U F6 F' U U B L U' F U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U R
R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U' x x U' F' z z F' z x U' D D F U' R U z F U F' U' R z z U U F U' F' U' R U z' U R z F F U F F U' F F U' D R' D R D U U y' F R' F' y D D y' F R F' y D U U D y' F R F' y D U y' F R F' y D U' D U' y' F R' F' y D D y' F R F' y U y y U L' U L U F U' F' U U z U' z U' z U' U' L' U L U F U' F' z U' z U' z U' U' L' U L U F U' F' U U z U' z U' z U' U' L' U L U F U' F' U' F R U R' U' R U R' U' F' R' U' R U' R' U U R U R' U' R U' R' U U R' F F R' B' R F F R' B R' U R R F F R' B' R F F R' B R' z z U' R' U' R U' R' U U R z z F U F' U F U U F' R' U' R U' R' U U R z z F U F' U F U U F' U U R' U' R U' R' U U R z z F U F' U F U U F' U U D

四,九阶魔方
1,网页版
JS部分参考上文
HTML部分:
改成九阶魔方,不需要显示转动效果,也不用把每个操作的效果都显示出来,只需要每隔100次操作,按照操作完之后的数据,刷新魔方各个格子的颜色就行。一个操作序列执行,或者1次自动运行,不管是不是100的倍数,最后一次执行完都要刷新颜色
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>九阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>九阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R5'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(16, 12.5, 20.5);
camera.lookAt(0, 0, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 九阶坐标 ----------
const N = 9;
const OUT = 4.0;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-4.0 + i);
}
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块(含逻辑状态) ----------
let cubies = [];
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return {
mesh,
home: new THREE.Vector3(x, y, z),
colors,
pos: new THREE.Vector3(x, y, z), // 逻辑位置
quat: new THREE.Quaternion() // 逻辑朝向
};
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 无动画即时旋转 ----------
const AXIS_VEC = {
x: new THREE.Vector3(1, 0, 0),
y: new THREE.Vector3(0, 1, 0),
z: new THREE.Vector3(0, 0, 1)
};
function snap(v) { return Math.round(v * 2) / 2; }
function rotateCubie(c, axis, angle) {
const q = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC[axis], angle);
c.pos.applyQuaternion(q);
c.pos.x = snap(c.pos.x);
c.pos.y = snap(c.pos.y);
c.pos.z = snap(c.pos.z);
c.quat.premultiply(q);
}
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return [];
return cubies.filter(c => {
const val = c.pos[def.axis];
return def.targets.some(t => Math.abs(val - t) < 0.01);
});
}
function applyInstantMove(faceKey, dir) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
const angle = -dir * Math.PI / 2;
cubies.forEach(c => rotateCubie(c, axis, angle));
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) return;
const selected = selectCubiesForLayer(faceKey);
const angle = dir * def.sign * Math.PI / 2;
selected.forEach(c => rotateCubie(c, def.axis, angle));
}
// ---------- 视觉同步(仅在需要刷新时调用) ----------
function syncMeshes() {
for (const c of cubies) {
c.mesh.position.copy(c.pos);
c.mesh.quaternion.copy(c.quat);
}
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取(基于逻辑状态) ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.pos.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.quat.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
function seqToText(seq) {
return seq.length ? seq.map(moveNotation).join(' ') : '(无)';
}
// ---------- 棋谱 ----------
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function buildRecordText() {
const lines = [];
lines.push('九阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
function refreshVisual() {
syncMeshes();
updateRecordPanel();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])([2-8])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = layerNum === '' ? letter : letter + layerNum;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
moves.push({ face: rotMatch[1], dir: rotMatch[2] === "'" ? -1 : 1 });
continue;
}
errors.push('无法解析: ' + raw);
}
return { moves, errors };
}
async function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
for (let i = 0; i < moves.length; i++) {
const mv = moves[i];
const isRot = !!ROT_DEF[mv.face];
applyInstantMove(mv.face, mv.dir);
userSeq.push({ face: mv.face, dir: mv.dir });
if (!isRot) moveCount++;
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === moves.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
// 让出主线程,避免大批量执行时界面卡死
await new Promise(r => setTimeout(r, 0));
}
}
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved', 'error');
}
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
await executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
cubies.forEach(c => {
c.pos.copy(c.home);
c.quat.identity();
});
syncMeshes();
}
function generateScramble(n = 100) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
async function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(100);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
for (let i = 0; i < scrambleSeq.length; i++) {
applyInstantMove(scrambleSeq[i].face, scrambleSeq[i].dir);
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === scrambleSeq.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
refreshVisual();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(key, dir);
userSeq.push({ face: key, dir });
moveCount++;
refreshVisual();
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved', 'error');
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(face, dir);
userSeq.push({ face, dir });
refreshVisual();
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原
(另一次打乱之后的复原)


五,十阶魔方
1,网页版
JS部分参考上文
HTML部分:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>十阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>十阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R5'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(18, 14, 23);
camera.lookAt(0, 0, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 十阶坐标 ----------
const N = 10;
const OUT = 4.5;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-4.5 + i);
}
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块(含逻辑状态) ----------
let cubies = [];
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return {
mesh,
home: new THREE.Vector3(x, y, z),
colors,
pos: new THREE.Vector3(x, y, z),
quat: new THREE.Quaternion()
};
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 无动画即时旋转 ----------
const AXIS_VEC = {
x: new THREE.Vector3(1, 0, 0),
y: new THREE.Vector3(0, 1, 0),
z: new THREE.Vector3(0, 0, 1)
};
function snap(v) { return Math.round(v * 2) / 2; }
function rotateCubie(c, axis, angle) {
const q = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC[axis], angle);
c.pos.applyQuaternion(q);
c.pos.x = snap(c.pos.x);
c.pos.y = snap(c.pos.y);
c.pos.z = snap(c.pos.z);
c.quat.premultiply(q);
}
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return [];
return cubies.filter(c => {
const val = c.pos[def.axis];
return def.targets.some(t => Math.abs(val - t) < 0.01);
});
}
function applyInstantMove(faceKey, dir) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
const angle = -dir * Math.PI / 2;
cubies.forEach(c => rotateCubie(c, axis, angle));
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) return;
const selected = selectCubiesForLayer(faceKey);
const angle = dir * def.sign * Math.PI / 2;
selected.forEach(c => rotateCubie(c, def.axis, angle));
}
function syncMeshes() {
for (const c of cubies) {
c.mesh.position.copy(c.pos);
c.mesh.quaternion.copy(c.quat);
}
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取(基于逻辑状态) ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.pos.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.quat.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
// 每 1000 个操作换行一次,单个操作(如 R4')不被换行符打断
function seqToText(seq) {
if (!seq.length) return '(无)';
const lines = [];
let cur = [];
for (const m of seq) {
cur.push(moveNotation(m));
if (cur.length === 1000) {
lines.push(cur.join(' '));
cur = [];
}
}
if (cur.length) lines.push(cur.join(' '));
return lines.join('\n');
}
// ---------- 棋谱 ----------
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function buildRecordText() {
const lines = [];
lines.push('十阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
function refreshVisual() {
syncMeshes();
updateRecordPanel();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])([2-9])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = layerNum === '' ? letter : letter + layerNum;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
moves.push({ face: rotMatch[1], dir: rotMatch[2] === "'" ? -1 : 1 });
continue;
}
errors.push('无法解析: ' + raw);
}
return { moves, errors };
}
async function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
for (let i = 0; i < moves.length; i++) {
const mv = moves[i];
const isRot = !!ROT_DEF[mv.face];
applyInstantMove(mv.face, mv.dir);
userSeq.push({ face: mv.face, dir: mv.dir });
if (!isRot) moveCount++;
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === moves.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved', 'error');
}
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
await executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
cubies.forEach(c => {
c.pos.copy(c.home);
c.quat.identity();
});
syncMeshes();
}
function generateScramble(n = 100) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
async function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(100);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
for (let i = 0; i < scrambleSeq.length; i++) {
applyInstantMove(scrambleSeq[i].face, scrambleSeq[i].dir);
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === scrambleSeq.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
refreshVisual();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(key, dir);
userSeq.push({ face: key, dir });
moveCount++;
refreshVisual();
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved', 'error');
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(face, dir);
userSeq.push({ face, dir });
refreshVisual();
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原

R5' D4 R3' L9 U8' D3 B2' D2' F2 R4' L7' B8 F6' B9 L6 U6' D9 R8' D8' U U9 U5' R6' B9 L6' L4' B6' L4' U3 F4' R4 L5' D8 L2 L5' B4 B9' U2' U3' B8' R7' B8' R' L6 D9 F9' U3' R' L3 L7' U6' R6 D2' U8 B7 R6 B2 L6' L8 B5 B8' B5' F5' U6 L3 U2 L2 U4 L7' U4' B8' R9 F7 R9' R2' D9' F6' F5' B8 L4' D L4' F4' U7 F7 B5' B2' D7' D3 D8 B' U7 R9' F4' D4' B2' U8' F5' D7 B7 x z' x F F U U L5' U' L5 U' L5' U U L5 F F z F F U U L5' U' L5 U' L5' U U L5 F F U L5' U' L5 U' L5' U U L5 z' x x z z F U U L5' U' L5 U' L5' U U L5 F' z F F U U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F z z x z F' U L5' U' L5 U' L5' U U L5 F x' F' U L5' U' L5 U' L5' U U L5 F z z F U U L5' U' L5 U' L5' U U L5 F U' L5' U' L5 U' L5' U U L5 F F x x U' L5' U' L5 U' L5' U U L5 x' F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F x F U L5' U' L5 U' L5' U U L5 F' z x z F F R5 R4' U R4 R3' U' R5' R4 U R4' F U R5 R4' U R4 R3' U' R5' R4 U R4' F z F R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F F z F U' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F' z x x F F R6 R5' U R4 R3' U' R6' R5 U R4' F F z F F R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F z F' R5 R4' U R4 R3' U' R5' R4 U R4' F U' R6 R5' U R4 R3' U' R6' R5 U R4' z F F U' R5 R4' U R4 R3' U' R5' R4 U R4' F F x z F F R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' x' F U R6 R5' U R4 R3' U' R6' R5 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F z z F F R6 R5' U R4 R3' U' R6' R5 U R4' F U R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' x x F F R5 R4' U R4 R3' U' R5' R4 U R4' F F R6 R5' U R4 R3' U' R6' R5 U R4' x' F U R5 R4' U R4 R3' U' R5' R4 U R4' F U R6 R5' U R4 R3' U' R6' R5 U R4' U U R5 R4' U R4 R3' U' R5' R4 U R4' F U U R6 R5' U R4 R3' U' R6' R5 U R4' F x F F R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F F U' R6 R5' U R4 R3' U' R6' R5 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' z x z F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 z z F U U L4' U' L4 U' L4' U U L4 F' z x x F F U U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 z' F F U U L4' U' L4 U' L4' U U L4 F F x z F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 F x' F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F L4' U' L4 U' L4' U U L4 z z x x F U U L4' U' L4 U' L4' U U L4 F' x' F U L4' U' L4 U' L4' U U L4 F F U' L4' U' L4 U' L4' U U L4 F U' L4' U' L4 U' L4' U U L4 x F' U' L4' U' L4 U' L4' U U L4 F U' L4' U' L4 U' L4' U U L4 z x x F R7 R6' U R3 R2' U' R7' R6 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F x' F R7 R6' U R3 R2' U' R7' R6 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F z R4 R3' U R3
R2' U' R4' R3 U R3' z F F R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' U U R4 R3' U R3 R2' U' R4' R3 U R3' z F U R6 R5' U R3 R2' U' R6' R5 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' z x x F U R7 R6' U R3 R2' U' R7' R6 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F U R6 R5' U R3 R2' U' R6' R5 U R3' z z F R4 R3' U R3 R2' U' R4' R3 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F F R6 R5' U R3 R2' U' R6' R5 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' z F R6 R5' U R3 R2' U' R6' R5 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' x z F R7 R6' U R3 R2' U' R7' R6 U R3' F U R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F x' F R7 R6' U R3 R2' U' R7' R6 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U U R6 R5' U R3 R2' U' R6' R5 U R3' z z F U R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U' R7 R6' U R3 R2' U' R7' R6 U R3' F F x x F U U R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F F R5 R4' U R3 R2' U' R5' R4 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' x' F R6 R5' U R3 R2' U' R6' R5 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' U' R4 R3' U R3 R2' U' R4' R3 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' x F U U R7 R6' U R3 R2' U' R7' R6 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' F U U R4 R3' U R3 R2' U' R4' R3 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' z x x U L3' U' L3 U' L3' U U L3 x' F' U L3' U' L3 U' L3' U U L3 F z F U U L3' U' L3 U' L3' U U L3 F' z F F U U L3' U' L3 U' L3' U U L3 F F z F F L3' U' L3 U' L3' U U L3 F F z x x U U L3' U' L3 U' L3' U U L3 z F F U U L3' U' L3 U' L3' U U L3 F F z F' L3' U' L3 U' L3' U U L3 F z x z F U L3' U' L3 U' L3' U U L3 F' U L3' U' L3 U' L3' U U L3 x' F F U U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 z z x x F F U L3' U' L3 U' L3' U U L3 F F U L3' U' L3 U' L3' U U L3 x' F' U U L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 x F F U U L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 F z x x F R5 R4' U R2 R' U' R5' R4 U R2' F R4 R3' U R2 R' U' R4' R3 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F x' F U R4 R3' U R2 R' U' R4' R3 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6'
R5 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' z F F R6 R5' U R2 R' U' R6' R5 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F z F U U R7 R6' U R2 R' U' R7' R6 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F R4 R3' U R2 R' U' R4' R3 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' R5 R4' U R2 R' U' R5' R4 U R2' z F' U' R3 R2' U R2 R' U' R3' R2 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' z x x F' R5 R4' U R2 R' U' R5' R4 U R2' F z F R8 R7' U R2 R' U' R8' R7 U R2' F' R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' z F R5 R4' U R2 R' U' R5' R4 U R2' F F R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' z F U R6 R5' U R2 R' U' R6' R5 U R2' F U U R3 R2' U R2 R' U' R3' R2 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' U' R7 R6' U R2 R' U' R7' R6 U R2' U' R4 R3' U R2 R' U' R4' R3 U R2' x z F R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' F x' F R4 R3' U R2 R' U' R4' R3 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' U' R6 R5' U R2 R' U' R6' R5 U R2' z z F R5 R4' U R2 R' U' R5' R4 U R2' F U U R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F U' R5 R4' U R2 R' U' R5' R4 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F x x F R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F U R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R5 R4' U R2 R' U' R5' R4 U R2' x' F R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' F U' R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U' R3 R2' U R2 R' U' R3' R2 U R2' F x F F U' R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' U'
R5 R4' U R2 R' U' R5' R4 U R2' z x x F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F x' F' U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 z F F L2' U' L2 U' L2' U U L2 F F z' x x z z F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F F L2' U' L2 U' L2' U U L2 z x z F F U L2' U' L2 U' L2' U U L2 F F x' F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F z z F' U' L2' U' L2 U' L2' U U L2 F x x F U L2' U' L2 U' L2' U U L2 F' x' F F U U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 x F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 R3 R2' U U R3 R2' U U R3' R2 x' L3' L2 U U L3 L2' x' U U R3 R2' U U R3' R2 R4 R3' U U R4 R3' U U R4' R3 x' L4' L3 U U L4 L3' x' U U R4 R3' U U R4' R3 U F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F5' F R U' R' U F5 F' U U F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' L R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U' D B D B U B U U L U R R B U' R B B z z x' F5' F R U' R' U F5 U L U' B U B U B U D B B U B L U B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U' x x U F U L U' B U B U B U D B B U' D B D B U B U' B L U' z' x' F5' F R U' R' U F5 F' R' z' x' L' B D' F5' F R U' R' U F5 F' U' x x U' F' z z F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U B L U B L U' z' x' F6' F R U' R' U F6 F' R' z' x' L' B D' F6' F R U' R' U F6 F' U U B L U' F U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U'
F U L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U B L L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L U' z' x' F6' F R U' R' U F6 F' R' z' x' L' B D' F6' F R U' R' U F6 F' U U B L U' F U L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U B L L U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L U' z' x' F6' F R U' R' U F6 F' R' z' x' L' B D' F6' F R U' R' U F6 F' U U B L U' F U L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U L U' B U B U B U D B B U' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L U' B U B U B U D B B R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L U' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U B L L U' B U B U B U D B B U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z
z x' F6' F R U' R' U F6 U L U U B L L U' B U B U B U D B B U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U L U' B U B U B U D B B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U' x x U' F' z z F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U U B L U B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' B L U B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U U B L L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B
B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U U B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U' R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L B L U B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U B L U B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L U' B U B U B U D B B R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B' L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B' L U' z' x' F7' F R U' R' U F7 F' R' z' x' L' B D' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F7' F R U' R' U F7 U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B L U' B U B U B U D B B U' D B D B U B U U R
R B U' R B B z z x' F7' F R U' R' U F7 U L U U L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F7' F R U' R' U F7 F' U' x x U' F' z z F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U B L U' z' x' F8' F R U' R' U F8 F' R' z' x' L' B D' F8' F R U' R' U F8 F' U U B L U' F U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U U B L L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' B L L U U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' B L L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B L U U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U L U
R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' B L L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L B L L U' B U B U B U D B B U U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F8' F R U' R' U F8 U L U U B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' B L L U' B U B U B U D B B U' D B D B U B U U R R B U' R B B z z x' F8' F R U' R' U F8 F' U' x x U' F' z z F' z' x U' F' U' R U' D' U' F F U D U F F U' z U' F' U y' F R F' y D U' D D U' y' F R' F' y D D y' F R F' y D U D' U U R' D R D U U D U U y' F R F' y D U U D' y' F R F' y D' U R' D R D U' D U y' F R F' y U' y y U U z U' z R U' R' U' F' U F U L' U L U F U' F' z U' z U' z U' U' L' U L U F U' F' z z U R U' R' U' F' U F z U' L' U L U F U' F' z U' z U' z R U' R' U' F' U F U F R U R' U' F' R' U' R U' R' U U R F U F' U F U U F' U U R R F F R' B' R F F R' B R' z' R' U' R U' R' U U R z z F U F' U F U U F' U U R' U' R U' R' U U R z z F U F' U F U U F' U

六,十一阶魔方
1,网页版
JS部分参考上文
HTML部分:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>十一阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>十一阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R5'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(20, 15.5, 25.5);
camera.lookAt(0, 0, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 十一阶坐标 ----------
const N = 11;
const OUT = 5.0;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-5.0 + i);
}
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块(含逻辑状态) ----------
let cubies = [];
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return {
mesh,
home: new THREE.Vector3(x, y, z),
colors,
pos: new THREE.Vector3(x, y, z),
quat: new THREE.Quaternion()
};
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 无动画即时旋转 ----------
const AXIS_VEC = {
x: new THREE.Vector3(1, 0, 0),
y: new THREE.Vector3(0, 1, 0),
z: new THREE.Vector3(0, 0, 1)
};
function snap(v) { return Math.round(v * 2) / 2; }
function rotateCubie(c, axis, angle) {
const q = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC[axis], angle);
c.pos.applyQuaternion(q);
c.pos.x = snap(c.pos.x);
c.pos.y = snap(c.pos.y);
c.pos.z = snap(c.pos.z);
c.quat.premultiply(q);
}
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return [];
return cubies.filter(c => {
const val = c.pos[def.axis];
return def.targets.some(t => Math.abs(val - t) < 0.01);
});
}
function applyInstantMove(faceKey, dir) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
const angle = -dir * Math.PI / 2;
cubies.forEach(c => rotateCubie(c, axis, angle));
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) return;
const selected = selectCubiesForLayer(faceKey);
const angle = dir * def.sign * Math.PI / 2;
selected.forEach(c => rotateCubie(c, def.axis, angle));
}
function syncMeshes() {
for (const c of cubies) {
c.mesh.position.copy(c.pos);
c.mesh.quaternion.copy(c.quat);
}
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取(基于逻辑状态) ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.pos.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.quat.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
// 每 1000 个操作换行一次,单个操作(如 R4')不被换行符打断
function seqToText(seq) {
if (!seq.length) return '(无)';
const lines = [];
let cur = [];
for (const m of seq) {
cur.push(moveNotation(m));
if (cur.length === 1000) {
lines.push(cur.join(' '));
cur = [];
}
}
if (cur.length) lines.push(cur.join(' '));
return lines.join('\n');
}
// ---------- 棋谱 ----------
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function buildRecordText() {
const lines = [];
lines.push('十一阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
function refreshVisual() {
syncMeshes();
updateRecordPanel();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])(10|[2-9])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = layerNum === '' ? letter : letter + layerNum;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
moves.push({ face: rotMatch[1], dir: rotMatch[2] === "'" ? -1 : 1 });
continue;
}
errors.push('无法解析: ' + raw);
}
return { moves, errors };
}
async function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
for (let i = 0; i < moves.length; i++) {
const mv = moves[i];
const isRot = !!ROT_DEF[mv.face];
applyInstantMove(mv.face, mv.dir);
userSeq.push({ face: mv.face, dir: mv.dir });
if (!isRot) moveCount++;
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === moves.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved', 'error');
}
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
await executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
cubies.forEach(c => {
c.pos.copy(c.home);
c.quat.identity();
});
syncMeshes();
}
function generateScramble(n = 100) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
async function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(100);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
for (let i = 0; i < scrambleSeq.length; i++) {
applyInstantMove(scrambleSeq[i].face, scrambleSeq[i].dir);
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === scrambleSeq.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
refreshVisual();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(key, dir);
userSeq.push({ face: key, dir });
moveCount++;
refreshVisual();
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved', 'error');
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(face, dir);
userSeq.push({ face, dir });
refreshVisual();
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原

【完整操作序列】
B9' R7' L8 B10 L4 R2' B4 L6' F5 B8 L7' U4' L9 F4' F3' B9 F' D9 R9' F7 B3 D9' D3 D4' B2 L B8' F5 F8' L4 U7 F B9 U10 U8 R4 U R L9' D8 L9' U9' D10 R7' F8 L4' F2 R10' D3 F4 B3' R4' R6 F5 D10 B6 B' F4 U' L2' L' R9 L' D4 B10 D2' D5' F10' F3 D5 R3' B2' L5 L10 R L8' D3' L10 L7 D7' U4 L6' U6 R2 F4' R2' U3' U2 R4 B8 U3 U6' B2 U10' B5 R8' D6' D5 D6 D7' F' R6 R5' U R5 R4' U' R6' R5 U R5' F z R6 R5' U R5 R4' U' R6' R5 U R5' z F F U R6 R5' U R5 R4' U' R6' R5 U R5' F F z F F R6 R5' U R5 R4' U' R6' R5 U R5' F F z x x z F R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F F z z F F U' R6 R5' U R5 R4' U' R6' R5 U R5' F F x z F R6 R5' U R5 R4' U' R6' R5 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' F x' F F U U R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F z z x x F R6 R5' U R5 R4' U' R6' R5 U R5' F' x' F R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' F x F R6 R5' U R5 R4' U' R6' R5 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' F z x x U L5' U' L5 U' L5' U U L5 x' F' U L5' U' L5 U' L5' U U L5 F z z F F U L5' U' L5 U' L5' U U L5 F F z F F U U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F z x x z F F U L5' U' L5 U' L5' U U L5 F F L5' U' L5 U' L5' U U L5 z z x z F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F F U L5' U' L5 U' L5' U U L5 x' F F U U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 z z x F F U U L5' U' L5 U' L5' U U L5 F F x F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F F L5' U' L5 U' L5' U U L5 z x x F F R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' x' F R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' z F F R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U R7 R6' U R4 R3' U' R7' R6 U R4' F z F U R5 R4' U R4 R3' U' R5' R4 U R4' F F U U R7 R6' U R4 R3' U' R7' R6 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' F z z x x F R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F z z F U R7 R6' U R4 R3' U' R7' R6 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F F R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F z x z F U R7 R6' U R4 R3' U' R7' R6 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F F x' F R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' z z F F U R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' x x F U R7 R6' U R4 R3' U' R7' R6 U R4' U R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' R5 R4' U R4 R3' U' R5' R4
U R4' F' R6 R5' U R4 R3' U' R6' R5 U R4' x' F R7 R6' U R4 R3' U' R7' R6 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U U R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U U R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' x F R7 R6' U R4 R3' U' R7' R6 U R4' U' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F F U' R7 R6' U R4 R3' U' R7' R6 U R4' U U R5 R4' U R4 R3' U' R5' R4 U R4' F z x x F' U L4' U' L4 U' L4' U U L4 F x' F' U L4' U' L4 U' L4' U U L4 F z z F F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 z L4' U' L4 U' L4' U U L4 z x x F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 F z F U' L4' U' L4 U' L4' U U L4 F' z z F' U L4' U' L4 U' L4' U U L4 F x z x' F F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 z z F U U L4' U' L4 U' L4' U U L4 F' x F U L4' U' L4 U' L4' U U L4 F F U L4' U' L4 U' L4' U U L4 F x F' U U L4' U' L4 U' L4' U U L4 F U' L4' U' L4 U' L4' U U L4 z x x F U R8 R7' U R3 R2' U' R8' R7 U R3' F' R4 R3' U R3 R2' U' R4' R3 U R3' x' F R8 R7' U R3 R2' U' R8' R7 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' z F R8 R7' U R3 R2' U' R8' R7 U R3' U' R4 R3' U R3 R2' U' R4' R3 U R3' F F R5 R4' U R3 R2' U' R5' R4 U R3' F z F U R7 R6' U R3 R2' U' R7' R6 U R3' F' z F F U' R8 R7' U R3 R2' U' R8' R7 U R3' U U R6 R5' U R3 R2' U' R6' R5 U R3' F F R7 R6' U R3 R2' U' R7' R6 U R3' U' R6 R5' U R3 R2' U' R6' R5 U R3' z x x F R8 R7' U R3 R2' U' R8' R7 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' F F R5 R4' U R3 R2' U' R5' R4 U R3' z F R5 R4' U R3 R2' U' R5' R4 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' U U R4 R3' U R3 R2' U' R4' R3 U R3' F z F R7 R6' U R3 R2' U' R7' R6 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' F U R6 R5' U R3 R2' U' R6' R5 U R3' z F R5 R4' U R3 R2' U' R5' R4 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' F' x z F R6 R5' U R3 R2' U' R6' R5 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' F x' F R7 R6' U R3 R2' U' R7' R6 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F U U R5 R4' U R3 R2' U' R5' R4 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' z z F U' R7 R6' U R3 R2' U' R7' R6 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' F U' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' x x F U R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F U U R8 R7' U R3 R2' U' R8' R7 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U
R5 R4' U R3 R2' U' R5' R4 U R3' x' F R8 R7' U R3 R2' U' R8' R7 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F U R8 R7' U R3 R2' U' R8' R7 U R3' U R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U U R6 R5' U R3 R2' U' R6' R5 U R3' x F R8 R7' U R3 R2' U' R8' R7 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U U R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U' R8 R7' U R3 R2' U' R8' R7 U R3' U U R7 R6' U R3 R2' U' R7' R6 U R3' U' R5 R4' U R3 R2' U' R5' R4 U R3' U U R4 R3' U R3 R2' U' R4' R3 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' U U R6 R5' U R3 R2' U' R6' R5 U R3' z x F U L3' U' L3 U' L3' U U L3 F' z z F F U L3' U' L3 U' L3' U U L3 F F U' L3' U' L3 U' L3' U U L3 z z x x F' U L3' U' L3 U' L3' U U L3 F z F U U L3' U' L3 U' L3' U U L3 F' z F' U U L3' U' L3 U' L3' U U L3 F z F L3' U' L3 U' L3' U U L3 F' x z U' L3' U' L3 U' L3' U U L3 x' F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F z z F' U U L3' U' L3 U' L3' U U L3 F x x F U L3' U' L3 U' L3' U U L3 F' x' F U L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 F x F F U U L3' U' L3 U' L3' U U L3 F F U L3' U' L3 U' L3' U U L3 z x x F R6 R5' U R2 R' U' R6' R5 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F x' F R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' z F U R8 R7' U R2 R' U' R8' R7 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F F z F U U R4 R3' U R2 R' U' R4' R3 U R2' F U R5 R4' U R2 R' U' R5' R4 U R2' F F R9 R8' U R2 R' U' R9' R8 U R2' R7 R6' U R2 R' U' R7' R6 U R2' z F U' R6 R5' U R2 R' U' R6' R5 U R2' F' U' R7 R6' U R2 R' U' R7' R6 U R2' z x x F R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' F R4 R3' U R2 R' U' R4' R3 U R2' F z F' R6 R5' U R2 R' U' R6' R5 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' z F R9 R8' U R2 R' U' R9' R8 U R2' U U R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F F R7 R6' U R2 R' U' R7' R6 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' z F R9 R8' U R2
R' U' R9' R8 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F U R3 R2' U R2 R' U' R3' R2 U R2' F U' R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' x z F R7 R6' U R2 R' U' R7' R6 U R2' F U R7 R6' U R2 R' U' R7' R6 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R3 R2' U R2 R' U' R3' R2 U R2' x' F U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' U U R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' z z F U' R9 R8' U R2 R' U' R9' R8 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F U R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' x x F R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' x' F U R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R3 R2' U R2 R' U' R3' R2 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F U' R9 R8' U R2 R' U' R9' R8 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' x F R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' U' R7 R6' U R2 R' U' R7' R6 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' z x x F' U L2' U' L2 U' L2' U U L2 F x' F' U L2' U' L2 U' L2' U U L2 F z F U' L2' U' L2 U' L2' U U L2 F F U' L2' U' L2 U' L2' U U L2 F z' x x F U L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 F F U' L2' U' L2 U' L2' U U L2 z' x z F U L2' U' L2 U' L2' U U L2 F' U' L2' U' L2 U' L2' U U L2 x' F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F z z F' U U L2' U' L2 U' L2' U U L2 F x x F U U L2' U' L2 U' L2' U U L2 F' x' F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F x F F U L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 F R4 R3' U U R4 R3' U U R4' R3 x' L4' L3 U U L4 L3' x' U U R4 R3' U U R4' R3 U U B L U' F U L U' B U B U B U D
B B U' D B D B U B U' B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U U B L L R R B U' R B B z z x' F6' F R U' R' U F6 U L U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 F' U' B L U' F U L U' L R R B U' R B B z z x' F6' F R U' R' U F6 F' B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' D B D B U B B L L U' B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U U B L L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' D B D B U B B L L U' B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U U B L L U U R R B U' R B B z z x' F6' F R U' R' U F6 U U B L L U U R R B U' R B B z z x' F6' F R U' R' U F6 F' U' B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B' L L U' B U B U B U D B B U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B L U R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B U' L U' B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B' L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U
U B L U' F U L U' B U B U B U D B B U L U R R B U' R B B z z x' F7' F R U' R' U F7 F' U' x x U U B L U' F U L U' B U B U B U D B B U U B L L U' B U B U B U D B B U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B' L L U U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B' L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B' L L U' B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 F' B L U' F U L U' B U B U B U D B' L L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U' B L U' F U L U' R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B' L L U' B U B U B U D B B U R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B' L L U' B U B U B U D B B R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 U L U B L L U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U U B L
L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U B L L U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U L U R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' L R R B U' R B B z z x' F9' F R U' R' U F9 F' U' B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U R R B U' R B B z z x' F9' F R U' R' U F9 U L B L L U' B U B U B U D B B U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B' L L U' B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 F' z' x D U' F' U' R U U D U F F U' z' U' U' R U' D R' D R D D y' F R F' y D U' y' F R' F' y D D y' F R F' y D U D D U U y' F R' F' y D D y' F R F' y D U U D' U y' F R F' y D U' D D U y' F R F' y U' y y U z U' z U' U' L' U L U F U' F' U U z U' z R U' R' U' F' U F U U z U' z U' z R U' R' U' F' U F U L' U L U F U' R U R' U' R U R' U' F' R' U' R U' R' U U R U U R R F F R' B' R F F R' B R' R' U' R U' R' U U R z z F U F' U F U U F' U U R' U' R U' R' U U R z z F U F' U F U U F' D
七,十二阶魔方
1,网页版
JS部分参考上文
HTML部分:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>十二阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>十二阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R10'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(22, 17, 28);
camera.lookAt(0, 0, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 十二阶坐标 ----------
const N = 12;
const OUT = 5.5;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-5.5 + i);
}
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块(含逻辑状态) ----------
let cubies = [];
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return {
mesh,
home: new THREE.Vector3(x, y, z),
colors,
pos: new THREE.Vector3(x, y, z),
quat: new THREE.Quaternion()
};
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 无动画即时旋转 ----------
const AXIS_VEC = {
x: new THREE.Vector3(1, 0, 0),
y: new THREE.Vector3(0, 1, 0),
z: new THREE.Vector3(0, 0, 1)
};
function snap(v) { return Math.round(v * 2) / 2; }
function rotateCubie(c, axis, angle) {
const q = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC[axis], angle);
c.pos.applyQuaternion(q);
c.pos.x = snap(c.pos.x);
c.pos.y = snap(c.pos.y);
c.pos.z = snap(c.pos.z);
c.quat.premultiply(q);
}
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return [];
return cubies.filter(c => {
const val = c.pos[def.axis];
return def.targets.some(t => Math.abs(val - t) < 0.01);
});
}
function applyInstantMove(faceKey, dir) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
const angle = -dir * Math.PI / 2;
cubies.forEach(c => rotateCubie(c, axis, angle));
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) return;
const selected = selectCubiesForLayer(faceKey);
const angle = dir * def.sign * Math.PI / 2;
selected.forEach(c => rotateCubie(c, def.axis, angle));
}
function syncMeshes() {
for (const c of cubies) {
c.mesh.position.copy(c.pos);
c.mesh.quaternion.copy(c.quat);
}
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取(基于逻辑状态) ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.pos.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.quat.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
// 每 1000 个操作换行一次,单个操作(如 R11')不被换行符打断
function seqToText(seq) {
if (!seq.length) return '(无)';
const lines = [];
let cur = [];
for (const m of seq) {
cur.push(moveNotation(m));
if (cur.length === 1000) {
lines.push(cur.join(' '));
cur = [];
}
}
if (cur.length) lines.push(cur.join(' '));
return lines.join('\n');
}
// ---------- 棋谱 ----------
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function buildRecordText() {
const lines = [];
lines.push('十二阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
function refreshVisual() {
syncMeshes();
updateRecordPanel();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])(1[01]|[2-9])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = layerNum === '' ? letter : letter + layerNum;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
moves.push({ face: rotMatch[1], dir: rotMatch[2] === "'" ? -1 : 1 });
continue;
}
errors.push('无法解析: ' + raw);
}
return { moves, errors };
}
async function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
for (let i = 0; i < moves.length; i++) {
const mv = moves[i];
const isRot = !!ROT_DEF[mv.face];
applyInstantMove(mv.face, mv.dir);
userSeq.push({ face: mv.face, dir: mv.dir });
if (!isRot) moveCount++;
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === moves.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved', 'error');
}
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
await executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
cubies.forEach(c => {
c.pos.copy(c.home);
c.quat.identity();
});
syncMeshes();
}
function generateScramble(n = 100) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
async function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(100);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
for (let i = 0; i < scrambleSeq.length; i++) {
applyInstantMove(scrambleSeq[i].face, scrambleSeq[i].dir);
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === scrambleSeq.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
refreshVisual();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(key, dir);
userSeq.push({ face: key, dir });
moveCount++;
refreshVisual();
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved', 'error');
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(face, dir);
userSeq.push({ face, dir });
refreshVisual();
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原

【完整操作序列】
F10 R9' B7' L4 F' L4' D9' U9 R10 U4 D4 B5 F3 F9' R10 U3' L5' R5' U11' U9' F8' B7' R6' R' L3' L4' L8' F8 U10' U2' U8' L9' F5 U4 R8 B5 D' L5 U8 R4 U10' L11 B4' F6 L4' F5 F4' D6' D3' L4 B3' R4' U4' B10' D6 L5' L11 F11' R8 U4 R9' R8 U3 D4 F10' B4 U7' R7' F11 D2' F7 U5' B9' U11 U8 F3' R3 F' D9 R2 R8' D6' U2' R F L8' R L9 U3' U2' U10' F5 L9' L2 L10' D6 D10 R11 L U2 x F F U L6' U' L6 U' L6' U U L6 F F z F F U U L6' U' L6 U' L6' U U L6 F U L6' U' L6 U' L6' U U L6 F z F L6' U' L6 U' L6' U U L6 F' z z x x F F U L6' U' L6 U' L6' U U L6 F F z F F U U L6' U' L6 U' L6' U U L6 F F z z F U L6' U' L6 U' L6' U U L6 F' U L6' U' L6 U' L6' U U L6 z x z F U L6' U' L6 U' L6' U U L6 F' x' F U L6' U' L6 U' L6' U U L6 F F U L6' U' L6 U' L6' U U L6 F U L6' U' L6 U' L6' U U L6 z z x F F U' L6' U' L6 U' L6' U U L6 F U' L6' U' L6 U' L6' U U L6 F x U' L6' U' L6 U' L6' U U L6 z x x R6 R5' U R5 R4' U' R6' R5 U R5' x' F R7 R6' U R5 R4' U' R7' R6 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' z F F U R7 R6' U R5 R4' U' R7' R6 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F F z z F U U R6 R5' U R5 R4' U' R6' R5 U R5' F F U U R7 R6' U R5 R4' U' R7' R6 U R5' F z x x F R6 R5' U R5 R4' U' R6' R5 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' z F F U U R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' F z z F' U U R7 R6' U R5 R4' U' R7' R6 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' x z F F R7 R6' U R5 R4' U' R7' R6 U R5' F F x' F F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' z z F R7 R6' U R5 R4' U' R7' R6 U R5' F U R7 R6' U R5 R4' U' R7' R6 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' x x F F R7 R6' U R5 R4' U' R7' R6 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F F x' F R7 R6' U R5 R4' U' R7' R6 U R5' U U R7 R6' U R5 R4' U' R7' R6 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' F x F R7 R6' U R5 R4' U' R7' R6 U R5' R7 R6' U R5 R4' U' R7' R6 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' z x x F' U L5' U' L5 U' L5' U U L5 F x' F U' L5' U' L5 U' L5' U U L5 F F U' L5' U' L5 U' L5' U U L5 F x x F' U L5' U' L5 U' L5' U U L5 F z' F' L5' U' L5 U' L5' U U L5 F x z F U L5' U' L5 U' L5' U U L5 F F U L5' U' L5 U' L5' U U L5 F x' F' U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 z z F F U' L5' U' L5 U' L5' U U L5 F F x x F' U L5' U' L5 U' L5' U U L5 F x' F U L5' U' L5 U' L5' U U L5 F F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 x F U U L5' U' L5 U' L5' U U L5 F' U L5' U' L5 U' L5' U U L5 z x x F R8 R7' U R4 R3' U' R8' R7 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' F F R7 R6' U R4 R3' U' R7' R6 U R4' x' F R8 R7' U R4 R3' U' R8' R7 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F z F U R5 R4' U R4 R3' U' R5' R4 U R4' F F R7 R6' U R4
R3' U' R7' R6 U R4' F z F F R7 R6' U R4 R3' U' R7' R6 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F U R6 R5' U R4 R3' U' R6' R5 U R4' z F U U R8 R7' U R4 R3' U' R8' R7 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' F z x x F R5 R4' U R4 R3' U' R5' R4 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F F z F U R8 R7' U R4 R3' U' R8' R7 U R4' F F R8 R7' U R4 R3' U' R8' R7 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' z F R7 R6' U R4 R3' U' R7' R6 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' F U R7 R6' U R4 R3' U' R7' R6 U R4' F F z F U R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' x z F R8 R7' U R4 R3' U' R8' R7 U R4' F U R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U R8 R7' U R4 R3' U' R8' R7 U R4' F x' F R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' U R7 R6' U R4 R3' U' R7' R6 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' z z F U U R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F x x F U R8 R7' U R4 R3' U' R8' R7 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' U' R5 R4' U R4 R3' U' R5' R4 U R4' F F x' F R6 R5' U R4 R3' U' R6' R5 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U R8 R7' U R4 R3' U' R8' R7 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F U R8 R7' U R4 R3' U' R8' R7 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F U U R7 R6' U R4 R3' U' R7' R6 U R4' x F U R8 R7' U R4 R3' U' R8' R7 U R4' R8 R7' U R4 R3' U' R8' R7 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' U U R5 R4' U R4 R3' U' R5' R4 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' z x F F U L4' U' L4 U' L4' U U L4 F F z z F U L4' U' L4 U' L4' U U L4 F' z F F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F z x x F U L4' U' L4 U' L4' U U L4 F' z U' L4' U' L4 U' L4' U U L4 z z F' U L4' U' L4 U' L4' U U L4 F x z x' U U L4' U' L4 U' L4' U U L4 z z x F F U' L4' U' L4 U' L4' U U L4 F F U' L4' U' L4 U' L4' U U L4 x F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F F z x x F U R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U U R9 R8' U R3 R2' U' R9' R8 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' F U' R9 R8' U R3 R2' U' R9' R8 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' x' F R9 R8' U R3 R2' U' R9' R8 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' U R7 R6' U R3 R2' U'
R7' R6 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U R9 R8' U R3 R2' U' R9' R8 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' z F R7 R6' U R3 R2' U' R7' R6 U R3' F' U R4 R3' U R3 R2' U' R4' R3 U R3' z F U' R4 R3' U R3 R2' U' R4' R3 U R3' F U U R8 R7' U R3 R2' U' R8' R7 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F U R6 R5' U R3 R2' U' R6' R5 U R3' F U R6 R5' U R3 R2' U' R6' R5 U R3' z F R7 R6' U R3 R2' U' R7' R6 U R3' F F U' R5 R4' U R3 R2' U' R5' R4 U R3' F z x x F R4 R3' U R3 R2' U' R4' R3 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F z F F R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' U' R4 R3' U R3 R2' U' R4' R3 U R3' z F R6 R5' U R3 R2' U' R6' R5 U R3' F F R6 R5' U R3 R2' U' R6' R5 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' z F R8 R7' U R3 R2' U' R8' R7 U R3' U' R7 R6' U R3 R2' U' R7' R6 U R3' U U R6 R5' U R3 R2' U' R6' R5 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' F x z F U U R9 R8' U R3 R2' U' R9' R8 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' U' R4 R3' U R3 R2' U' R4' R3 U R3' x' F R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' U R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' U U R7 R6' U R3 R2' U' R7' R6 U R3' U U R6 R5' U R3 R2' U' R6' R5 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' z z F R9 R8' U R3 R2' U' R9' R8 U R3' U' R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' F F x x F R6 R5' U R3 R2' U' R6' R5 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' U' R6 R5' U R3 R2' U' R6' R5 U R3' F U R8 R7' U R3 R2' U' R8' R7 U R3' U' R7 R6' U R3 R2' U' R7' R6 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' x' F R9 R8' U R3 R2' U' R9' R8 U R3' U U R9 R8' U R3 R2' U' R9' R8 U R3' U R8 R7' U R3 R2' U' R8' R7 U R3' U R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U' R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' U R5 R4' U R3
R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F x F R9 R8' U R3 R2' U' R9' R8 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' U U R4 R3' U R3 R2' U' R4' R3 U R3' F U U R9 R8' U R3 R2' U' R9' R8 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' z x z F' U U L3' U' L3 U' L3' U U L3 F z U U L3' U' L3 U' L3' U U L3 z F' L3' U' L3 U' L3' U U L3 F z x x U L3' U' L3 U' L3' U U L3 z z F U L3' U' L3 U' L3' U U L3 F' z F F U U L3' U' L3 U' L3' U U L3 F F U L3' U' L3 U' L3' U U L3 x z F U L3' U' L3 U' L3' U U L3 F' x' F U L3' U' L3 U' L3' U U L3 F F U' L3' U' L3 U' L3' U U L3 F z z F U L3' U' L3 U' L3' U U L3 F' x x F U L3' U' L3 U' L3' U U L3 F F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 x' F F U L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 F U' L3' U' L3 U' L3' U U L3 x F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F U L3' U' L3 U' L3' U U L3 F z x x F R3 R2' U R2 R' U' R3' R2 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F x' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' R4 R3' U R2 R' U' R4' R3 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' z F F R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' F R4 R3' U R2 R' U' R4' R3 U R2' z F U R8 R7' U R2 R' U' R8' R7 U R2' F' z F F R9 R8' U R2 R' U' R9' R8 U R2' U U R7 R6' U R2 R' U' R7' R6 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' U' R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' z x x F R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' F R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R5 R4' U R2 R' U' R5' R4 U R2' z F R8 R7' U R2 R' U' R8' R7 U R2' F F R10 R9' U R2 R' U' R10' R9 U R2' F U R4 R3' U R2 R' U' R4' R3 U R2' z F U R6 R5' U R2 R' U' R6' R5 U R2' F F U R9 R8' U R2 R' U' R9' R8 U R2' F U R10 R9' U R2 R' U' R10' R9 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' z F U' R9 R8' U R2 R' U' R9' R8 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' F U U R8 R7' U R2 R' U' R8' R7 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' F U R10 R9' U R2 R' U' R10' R9 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' U' R6 R5' U R2 R' U' R6' R5 U R2' x z
F R9 R8' U R2 R' U' R9' R8 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' R9 R8' U R2 R' U' R9' R8 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' R3 R2' U R2 R' U' R3' R2 U R2' x' F U R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' U U R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' z z F U U R9 R8' U R2 R' U' R9' R8 U R2' F U' R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F U U R10 R9' U R2 R' U' R10' R9 U R2' U' R9 R8' U R2 R' U' R9' R8 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U U R7 R6' U R2 R' U' R7' R6 U R2' U' R6 R5' U R2 R' U' R6' R5 U R2' x x F R10 R9' U R2 R' U' R10' R9 U R2' R9 R8' U R2 R' U' R9' R8 U R2' R9 R8' U R2 R' U' R9' R8 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R3 R2' U R2 R' U' R3' R2 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' F x' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R10 R9' U R2 R' U' R10' R9 U R2' U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' F U' R9 R8' U R2 R' U' R9' R8 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' x F U R10 R9' U R2 R' U' R10' R9 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' U U R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F U R10 R9' U R2 R' U' R10' R9 U R2' U U R9 R8' U R2 R' U' R9' R8 U R2' U U R8 R7' U R2 R' U' R8' R7 U R2' U' R7 R6' U R2 R' U' R7' R6 U R2' U' R6 R5' U R2 R' U' R6' R5 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' U' R4 R3' U R2 R' U' R4' R3 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' z x x U L2' U' L2 U' L2' U U L2 x' F' U L2' U' L2 U' L2' U U L2 F z z F F U L2' U' L2 U' L2' U U L2 F U' L2' U' L2
U' L2' U U L2 F z z x x z z F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F F z F F U' L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 F x z F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 x' F F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 z z x x F F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 x' F' U U L2' U' L2 U' L2' U U L2 F U' L2' U' L2 U' L2' U U L2 x F' U L2' U' L2 U' L2' U U L2 F R2 R' U U R2 R' U U R2' R x' L2' L U U L2 L' x' U U R2 R' U U R2' R R5 R4' U U R5 R4' U U R5' R4 x' L5' L4 U U L5 L4' x' U U R5 R4' U U R5' R4 F U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U' D B D B U B U' B L L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' B U B U B U D B B U L U' B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L U' L U U B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L B L L U' B L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 U L L U' B U B U B U D B' L U' L' R R B U' R B B z z x' F6' F R U' R' U F6 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U' B L U' F U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U U L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U' B L U' F U L U' B U B U B U D B B U B L L U R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' B L L U' B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L B L L U U R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' L U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U L U R R B U' R B B z z x' F7' F R U' R' U F7 U L B L L U' B L
U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U U B L L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F8' F R U' R' U F8 U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B L L B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U L U R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' L U' B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B B L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U U L U R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U U B L L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L L U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U B L L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F
U L U' B U B U B U D B B U' D B D B U B U' L U R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' D B D B U B B L L U U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' L U U R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' D B D B U B U' L U' B U B U B U D B B U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U U L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U U B L L R R B U' R B B z z x' F10' F R U' R' U F10 U L B L L U R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U U L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' B U B U B U D B B U U B L L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B L L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U U B L L R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' B L L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U B L L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 F' U' B L U' F U L U' B U B U B U D B B R R B U' R B B z z x' F10' F R U' R' U F10 F' U' B L U' F U L U B L L B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F10' F R U' R' U F10 F' B L U' F U U B L L U' B U B U B U D B B U U R R B U' R B B z z x' F10' F R U' R' U F10 F' z x D D U U F F U U D D F F z' F U U F F U' z' F' U D D U' R' D R D U' y' F R' F' y D D y' F R F' y D U U D U' y' F R F' y D U D' U y' F R F' y D U'
D D R' D R D D y' F R F' y U y' F R' F' y D D y' F R F' y U' y y U z U' z U' z U' U' L' U L U F U' F' U z U' U' L' U L U F U' F' U U R U' R' U' F' U F U z U' z U' U' L' U L U F U' F' U U F R U R' U' R U R' U' F' U R' U' R U' R' U U R F U F' U F U U F' U R R F F R' B' R F F R' B R' z' U R' U' R U' R' U U R z z F U F' U F U U F' U' R' U' R U' R' U U R z z F U F' U F U U F' U D'
八,十三阶魔方
1,网页版
JS部分参考上文
HTML部分:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>十三阶魔方</title>
<style>
:root {
--bg1: #161b26;
--bg2: #1f2635;
--txt: #e8edf5;
--dim: #8fa0bf;
--panel: #1b2231;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: radial-gradient(1200px 700px at 50% -10%, var(--bg2), var(--bg1));
color: var(--txt);
padding: 6px 8px 8px;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
h1 {
margin: 0 0 2px;
font-size: 20px;
letter-spacing: 2px;
text-align: center;
flex-shrink: 0;
}
#status {
min-height: 18px;
margin: 0 0 4px;
font-size: 12px;
color: var(--dim);
transition: color .3s;
text-align: center;
flex-shrink: 0;
}
#status.solved { color: #7ee08a; font-weight: bold; animation: pop .55s ease; }
#status.error { color: #ff7a7a; font-weight: bold; }
@keyframes pop {
0% { transform: scale(.6); opacity: 0; }
70% { transform: scale(1.18); }
100% { transform: scale(1); opacity: 1; }
}
#main {
display: flex;
gap: 6px;
flex: 1;
min-height: 0;
width: 100%;
max-width: 100%;
margin: 0 auto;
}
#leftCol {
flex: 0 0 58%;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
}
#stage {
width: 100%;
height: 100%;
min-height: 0;
background: transparent;
border-radius: 8px;
overflow: hidden;
}
#rightCol {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
gap: 4px;
overflow: hidden;
}
#controls {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 4px;
align-items: center;
padding: 4px 6px 6px;
background: var(--panel);
border-radius: 8px;
border: 1px solid #2a3344;
}
.move-row {
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
button {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
background: #37425c;
color: #fff;
box-shadow: 0 2px 0 #1a2130;
transition: transform .06s, background .15s, box-shadow .06s;
min-width: 38px;
letter-spacing: .3px;
flex-shrink: 0;
}
button:hover { background: #46557a; }
button:active { transform: translateY(2px); box-shadow: 0 0 0 #1a2130; }
button.tool { background: #2f7a55; }
button.tool:hover { background: #3b9768; }
button.tool.warn { background: #8a3d3d; }
button.tool.warn:hover { background: #a84c4c; }
button.copy { background: #2a6e8f; }
button.copy:hover { background: #3588ad; }
#autoRunBtn { background: #7a3f8a; }
#autoRunBtn:hover { background: #9550a8; }
#seqInput {
flex: 1;
min-width: 60px;
padding: 5px 8px;
border-radius: 6px;
border: 1px solid #4a5a7a;
background: #161b26;
color: #e8edf5;
font-size: 12px;
font-family: "Cascadia Code", "Consolas", monospace;
width: 100%;
}
#seqInput:focus { outline: none; border-color: #6d88c8; }
#seqRow {
flex-wrap: nowrap;
width: 100%;
}
#recordBox {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
#recordTitle {
font-size: 13px;
font-weight: 700;
color: #cfd9ea;
letter-spacing: 0.5px;
text-align: center;
flex-shrink: 0;
margin: 0;
}
#recordPanel {
background: var(--panel);
border: 1px solid #2a3344;
border-radius: 8px;
padding: 6px 10px;
font-family: "Cascadia Code", "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.5;
color: #cfd9ea;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
min-height: 0;
overflow: auto;
margin: 0;
}
#copyBtn { width: 100%; flex-shrink: 0; padding: 5px 0; font-size: 12px; }
@media (max-width: 860px) {
body { padding: 4px 6px 6px; }
#main { flex-direction: column; gap: 6px; }
#leftCol { flex: 0 0 55vh; width: 100%; }
#rightCol { flex: 1; min-height: 260px; }
#controls { padding: 4px; }
button { padding: 5px 8px; font-size: 12px; min-width: 34px; }
#stage { height: 100%; }
#recordPanel { font-size: 10px; padding: 4px 8px; }
h1 { font-size: 18px; }
}
@media (max-width: 480px) {
#leftCol { flex: 0 0 45vh; }
#seqRow { flex-wrap: wrap; }
#seqInput { flex: 1 1 100%; }
}
</style>
</head>
<body>
<h1>十三阶魔方</h1>
<div id="status">加载中…</div>
<div id="main">
<div id="leftCol">
<div id="stage"></div>
</div>
<div id="rightCol">
<div id="controls">
<div class="move-row">
<button class="tool" id="scrambleBtn">打乱</button>
<button class="tool warn" id="resetBtn">复原</button>
<button class="tool" id="autoRunBtn">自动运行</button>
</div>
<div class="move-row" id="seqRow">
<input id="seqInput" type="text" placeholder="操作序列,如 U2' F3 R12'" />
<button class="tool" id="seqBtn">执行</button>
</div>
</div>
<div id="recordBox">
<div id="recordTitle">📋 棋谱</div>
<button class="tool copy" id="copyBtn">📋 一键复制棋谱</button>
<pre id="recordPanel"></pre>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 场景 ----------
const stage = document.getElementById('stage');
const statusEl = document.getElementById('status');
const recordPanel = document.getElementById('recordPanel');
const copyBtn = document.getElementById('copyBtn');
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
function resizeRenderer() {
const w = stage.clientWidth;
const h = stage.clientHeight;
if (w > 0 && h > 0) {
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
}
stage.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(30, stage.clientWidth / stage.clientHeight, 0.1, 100);
camera.position.set(24, 18.5, 30.5);
camera.lookAt(0, 0, 0);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));
const light1 = new THREE.DirectionalLight(0xffffff, 1.3);
light1.position.set(5, 9, 6);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 0.45);
light2.position.set(-4, -3, -2);
scene.add(light2);
// ---------- 颜色 ----------
const COL_HEX = {
U: '#f5f5f5',
D: '#ffd500',
R: '#3b82f6',
L: '#3aab4e',
F: '#e33c3c',
B: '#ff8c1a'
};
const COL_CHAR = { U: 'W', D: 'Y', R: 'B', L: 'G', F: 'R', B: 'O' };
// ---------- 十三阶坐标 ----------
const N = 13;
const OUT = 6.0;
const POSITIONS = [];
for (let i = 0; i < N; i++) {
POSITIONS.push(-6.0 + i);
}
const SIZE = 0.82;
const AXIS_MAP = {
U: { axis: 'y', side: 1, vec: new THREE.Vector3(0, 1, 0) },
D: { axis: 'y', side: -1, vec: new THREE.Vector3(0, -1, 0) },
R: { axis: 'x', side: 1, vec: new THREE.Vector3(1, 0, 0) },
L: { axis: 'x', side: -1, vec: new THREE.Vector3(-1, 0, 0) },
F: { axis: 'z', side: 1, vec: new THREE.Vector3(0, 0, 1) },
B: { axis: 'z', side: -1, vec: new THREE.Vector3(0, 0, -1) }
};
const FACE_SIGN = {
'R': -1, 'L': 1, 'U': -1, 'D': 1, 'F': -1, 'B': 1
};
function buildLayerDefs() {
const defs = {};
const faces = ['U','D','R','L','F','B'];
for (const f of faces) {
const base = AXIS_MAP[f];
for (let layer = 1; layer <= N-1; layer++) {
const key = layer === 1 ? f : f + layer;
let targets;
if (base.side === 1) {
targets = POSITIONS.slice(N - layer);
} else {
targets = POSITIONS.slice(0, layer);
}
defs[key] = {
axis: base.axis,
targets: targets,
face: f,
sign: FACE_SIGN[f]
};
}
}
return defs;
}
const LAYER_DEFS = buildLayerDefs();
const ROT_DEF = {
x: { axis: 'x' },
y: { axis: 'z' },
z: { axis: 'y' }
};
const FACE_ORDER = ['U','D','L','R','F','B'];
// ---------- 贴纸纹理 ----------
function roundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function makeStickerTexture(hex) {
const size = 128;
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0b0b0d';
roundedRect(ctx, 0, 0, size, size, 14);
ctx.fill();
const m = 17;
ctx.fillStyle = hex;
roundedRect(ctx, m, m, size - 2 * m, size - 2 * m, 9);
ctx.fill();
const tex = new THREE.CanvasTexture(canvas);
tex.colorSpace = THREE.SRGBColorSpace;
tex.anisotropy = 4;
return tex;
}
const TEX = {};
for (const f in COL_HEX) TEX[f] = makeStickerTexture(COL_HEX[f]);
const blackPlastic = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.62, metalness: 0.08 });
function stickerMaterial(face) {
return new THREE.MeshStandardMaterial({ map: TEX[face], roughness: 0.3, metalness: 0.05 });
}
// ---------- 表面块(含逻辑状态) ----------
let cubies = [];
function createCubie(x, y, z) {
const mats = [
x === OUT ? stickerMaterial('R') : blackPlastic,
x === -OUT ? stickerMaterial('L') : blackPlastic,
y === OUT ? stickerMaterial('U') : blackPlastic,
y === -OUT ? stickerMaterial('D') : blackPlastic,
z === OUT ? stickerMaterial('F') : blackPlastic,
z === -OUT ? stickerMaterial('B') : blackPlastic
];
const geo = new THREE.BoxGeometry(SIZE, SIZE, SIZE);
const mesh = new THREE.Mesh(geo, mats);
mesh.position.set(x, y, z);
scene.add(mesh);
const colors = {
px: x === OUT ? COL_CHAR.R : null,
nx: x === -OUT ? COL_CHAR.L : null,
py: y === OUT ? COL_CHAR.U : null,
ny: y === -OUT ? COL_CHAR.D : null,
pz: z === OUT ? COL_CHAR.F : null,
nz: z === -OUT ? COL_CHAR.B : null
};
return {
mesh,
home: new THREE.Vector3(x, y, z),
colors,
pos: new THREE.Vector3(x, y, z),
quat: new THREE.Quaternion()
};
}
function buildCube() {
cubies.forEach(c => scene.remove(c.mesh));
cubies = [];
for (const x of POSITIONS)
for (const y of POSITIONS)
for (const z of POSITIONS) {
if (Math.abs(x) === OUT || Math.abs(y) === OUT || Math.abs(z) === OUT) {
cubies.push(createCubie(x, y, z));
}
}
}
// ---------- 无动画即时旋转 ----------
const AXIS_VEC = {
x: new THREE.Vector3(1, 0, 0),
y: new THREE.Vector3(0, 1, 0),
z: new THREE.Vector3(0, 0, 1)
};
function snap(v) { return Math.round(v * 2) / 2; }
function rotateCubie(c, axis, angle) {
const q = new THREE.Quaternion().setFromAxisAngle(AXIS_VEC[axis], angle);
c.pos.applyQuaternion(q);
c.pos.x = snap(c.pos.x);
c.pos.y = snap(c.pos.y);
c.pos.z = snap(c.pos.z);
c.quat.premultiply(q);
}
function selectCubiesForLayer(faceKey) {
const def = LAYER_DEFS[faceKey];
if (!def) return [];
return cubies.filter(c => {
const val = c.pos[def.axis];
return def.targets.some(t => Math.abs(val - t) < 0.01);
});
}
function applyInstantMove(faceKey, dir) {
if (ROT_DEF[faceKey]) {
const axis = ROT_DEF[faceKey].axis;
const angle = -dir * Math.PI / 2;
cubies.forEach(c => rotateCubie(c, axis, angle));
return;
}
const def = LAYER_DEFS[faceKey];
if (!def) return;
const selected = selectCubiesForLayer(faceKey);
const angle = dir * def.sign * Math.PI / 2;
selected.forEach(c => rotateCubie(c, def.axis, angle));
}
function syncMeshes() {
for (const c of cubies) {
c.mesh.position.copy(c.pos);
c.mesh.quaternion.copy(c.quat);
}
}
// ---------- 工具函数 ----------
function axisToKey(v) {
const ax = Math.abs(v.x), ay = Math.abs(v.y), az = Math.abs(v.z);
if (ax >= ay && ax >= az) return v.x >= 0 ? 'px' : 'nx';
if (ay >= ax && ay >= az) return v.y >= 0 ? 'py' : 'ny';
return v.z >= 0 ? 'pz' : 'nz';
}
function isSolved() {
const st = getState();
for (const f of FACE_ORDER) {
const s = st[f];
if (!s || s.length !== N*N) return false;
for (let i = 1; i < N*N; i++) {
if (s[i] !== s[0]) return false;
}
}
return true;
}
// ---------- 局面读取(基于逻辑状态) ----------
function getFaceSlots(face) {
const s = [];
for (let r = 0; r < N; r++)
for (let c = 0; c < N; c++) {
const x = c - (N-1)/2;
const yTop = (N-1)/2 - r;
const z = r - (N-1)/2;
let p;
if (face === 'U') p = [x, OUT, z];
else if (face === 'D') p = [x, -OUT, z];
else if (face === 'F') p = [x, yTop, OUT];
else if (face === 'B') p = [-x, yTop, -OUT];
else if (face === 'R') p = [OUT, yTop, -x];
else p = [-OUT, yTop, x];
s.push(new THREE.Vector3(p[0], p[1], p[2]));
}
return s;
}
function faceColorString(face) {
const slots = getFaceSlots(face);
const n = AXIS_MAP[face].vec;
let out = '';
for (const p of slots) {
const cubie = cubies.find(c => c.pos.distanceTo(p) < 0.01);
if (!cubie) { out += '?'; continue; }
const n0 = n.clone().applyQuaternion(cubie.quat.clone().invert());
out += cubie.colors[axisToKey(n0)] || '?';
}
return out;
}
function getState() {
const state = {};
for (const f of FACE_ORDER) state[f] = faceColorString(f);
return state;
}
function stateToText(state) {
return FACE_ORDER.map(f => f + ': ' + state[f]).join('\n');
}
function moveNotation(m) {
return m.face + (m.dir === -1 ? "'" : '');
}
// 每 1000 个操作换行一次,单个操作(如 R12')不被换行符打断
function seqToText(seq) {
if (!seq.length) return '(无)';
const lines = [];
let cur = [];
for (const m of seq) {
cur.push(moveNotation(m));
if (cur.length === 1000) {
lines.push(cur.join(' '));
cur = [];
}
}
if (cur.length) lines.push(cur.join(' '));
return lines.join('\n');
}
// ---------- 棋谱 ----------
let moveCount = 0;
let scrambleSeq = [];
let userSeq = [];
let initialState = null;
function buildRecordText() {
const lines = [];
lines.push('十三阶魔方棋谱 · 层操作');
lines.push('');
lines.push('【打乱序列】');
lines.push(seqToText(scrambleSeq));
lines.push('');
lines.push('【初始局面(打乱后)】');
lines.push(initialState ? stateToText(initialState) : '(打乱中…)');
lines.push('');
lines.push('【当前局面】');
lines.push(stateToText(getState()));
lines.push('');
lines.push('【完整操作序列】');
lines.push(seqToText([...scrambleSeq, ...userSeq]));
return lines.join('\n');
}
function updateRecordPanel() {
recordPanel.textContent = buildRecordText();
}
function refreshVisual() {
syncMeshes();
updateRecordPanel();
}
async function copyRecord() {
const text = buildRecordText();
let ok = false;
try {
await navigator.clipboard.writeText(text);
ok = true;
} catch (e) {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try { ok = document.execCommand('copy'); } catch (_) {}
ta.remove();
}
const old = copyBtn.textContent;
copyBtn.textContent = ok ? '✅ 已复制' : '❌ 复制失败';
setTimeout(() => { copyBtn.textContent = old; }, 1500);
}
// ---------- 序列解析与执行 ----------
function parseSequence(text) {
const normalized = text.replace(/[’′]/g, "'");
const tokens = normalized.trim().split(/[\s,,]+/).filter(Boolean);
const moves = [];
const errors = [];
for (const raw of tokens) {
let m = raw.match(/^([URFDLB])(1[0-2]|[2-9])?([''])?$/);
if (m) {
const letter = m[1];
const layerNum = m[2] || '';
const dir = m[3] === "'" ? -1 : 1;
let faceKey = layerNum === '' ? letter : letter + layerNum;
if (!LAYER_DEFS[faceKey]) {
errors.push('未知操作: ' + raw);
continue;
}
moves.push({ face: faceKey, dir });
continue;
}
const rotMatch = raw.match(/^([xyz])([''])?$/);
if (rotMatch) {
moves.push({ face: rotMatch[1], dir: rotMatch[2] === "'" ? -1 : 1 });
continue;
}
errors.push('无法解析: ' + raw);
}
return { moves, errors };
}
async function executeSequence(text) {
const { moves, errors } = parseSequence(text);
if (errors.length > 0) {
statusEl.textContent = '❌ 非法输入: ' + errors.join('; ');
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
if (moves.length === 0) {
statusEl.textContent = '⚠️ 未检测到有效操作';
statusEl.classList.remove('solved', 'error');
return;
}
statusEl.classList.remove('error');
for (let i = 0; i < moves.length; i++) {
const mv = moves[i];
const isRot = !!ROT_DEF[mv.face];
applyInstantMove(mv.face, mv.dir);
userSeq.push({ face: mv.face, dir: mv.dir });
if (!isRot) moveCount++;
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === moves.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '序列执行完成(' + moves.length + ' 步操作)';
statusEl.classList.remove('solved', 'error');
}
}
// ---------- 自动运行 ----------
const SERVER = 'http://localhost:3000';
async function autoRun() {
const stateText = stateToText(getState());
try {
const res = await fetch(SERVER + '/write-input', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: stateText
});
if (!res.ok) throw new Error('write failed');
} catch (e) {
statusEl.textContent = '❌ 无法连接本地服务,请先运行 server.js 并从 http://localhost:3000 打开页面';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
statusEl.textContent = '已写入 D:/cubeInput.txt,等待 D:/cubeOutput.txt…';
statusEl.classList.remove('solved', 'error');
let content = '';
const maxWait = 120000;
const start = performance.now();
while (true) {
if (performance.now() - start > maxWait) {
statusEl.textContent = '等待 D:/cubeOutput.txt 超时';
statusEl.classList.add('error');
statusEl.classList.remove('solved');
return;
}
try {
const res = await fetch(SERVER + '/read-output');
const data = await res.json();
content = (data.content || '').trim();
} catch (e) {}
if (content) break;
await new Promise(r => setTimeout(r, 500));
}
await executeSequence(content);
}
// ---------- 打乱 / 复原 ----------
function resetCubeInternal() {
cubies.forEach(c => {
c.pos.copy(c.home);
c.quat.identity();
});
syncMeshes();
}
function generateScramble(n = 100) {
const faces = Object.keys(LAYER_DEFS);
const seq = [];
let last = null;
for (let i = 0; i < n; i++) {
let f, d;
do {
f = faces[Math.floor(Math.random() * faces.length)];
d = Math.random() < 0.5 ? 1 : -1;
} while (last && last === f);
seq.push({ face: f, dir: d });
last = f;
}
return seq;
}
async function scramble() {
resetCubeInternal();
userSeq = [];
initialState = null;
moveCount = 0;
statusEl.textContent = '打乱中…';
statusEl.classList.remove('solved', 'error');
scrambleSeq = generateScramble(100);
if (scrambleSeq.length === 0) {
statusEl.textContent = '打乱序列为空';
return;
}
for (let i = 0; i < scrambleSeq.length; i++) {
applyInstantMove(scrambleSeq[i].face, scrambleSeq[i].dir);
const isChunkEnd = (i + 1) % 100 === 0;
const isLast = i === scrambleSeq.length - 1;
if (isChunkEnd || isLast) {
refreshVisual();
await new Promise(r => setTimeout(r, 0));
}
}
initialState = getState();
moveCount = 0;
statusEl.textContent = '已打乱,开始复原吧';
statusEl.classList.remove('error');
}
function resetCube() {
resetCubeInternal();
moveCount = 0;
scrambleSeq = [];
userSeq = [];
initialState = null;
statusEl.textContent = '已还原到初始状态';
statusEl.classList.remove('solved', 'error');
refreshVisual();
}
// ---------- 按钮 & 键盘 ----------
function buildButtons() {
document.getElementById('scrambleBtn').addEventListener('click', scramble);
document.getElementById('resetBtn').addEventListener('click', resetCube);
copyBtn.addEventListener('click', copyRecord);
const seqInput = document.getElementById('seqInput');
const seqBtn = document.getElementById('seqBtn');
seqBtn.addEventListener('click', () => executeSequence(seqInput.value));
seqInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
executeSequence(seqInput.value);
}
});
document.getElementById('autoRunBtn').addEventListener('click', autoRun);
window.addEventListener('keydown', e => {
const key = e.key.toUpperCase();
if (/^[URFDLB]$/.test(key)) {
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(key, dir);
userSeq.push({ face: key, dir });
moveCount++;
refreshVisual();
if (isSolved()) {
statusEl.textContent = '🎉 已复原!共用了 ' + moveCount + ' 步';
statusEl.classList.add('solved');
statusEl.classList.remove('error');
} else {
statusEl.textContent = '步数:' + moveCount;
statusEl.classList.remove('solved', 'error');
}
return;
}
if (/^[XYZ]$/.test(key)) {
const face = key.toLowerCase();
const dir = e.shiftKey ? -1 : 1;
applyInstantMove(face, dir);
userSeq.push({ face, dir });
refreshVisual();
return;
}
});
}
// ---------- 渲染循环 ----------
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// ---------- 窗口自适应 ----------
function onResize() {
resizeRenderer();
}
window.addEventListener('resize', onResize);
const ro = new ResizeObserver(() => { resizeRenderer(); });
ro.observe(stage);
// ---------- 启动 ----------
buildCube();
buildButtons();
statusEl.textContent = '已就绪';
updateRecordPanel();
setTimeout(() => {
scramble();
}, 100);
animate();
setTimeout(resizeRenderer, 50);
</script>
</body>
</html>

2,自动复原

【完整操作序列】
D5' D4 L9' F9' F8' U D5' R' L9 L2' L8 L6 R4' F R11 R4' D5 U4 F9' L' B6' D10 L4' U12 L3' D7 U12 U5 L10' D12 D5 B2 R10 R12' B7 D8 B12' B10 L3 R12' F10' L8 B7' F4 L6' F8 L8 L5' U5' U9 B7' B9 F8' D10' F3' F10 F8 U11' F12' U8 L7' U10' L9 B3 L' R10 F2 F12 U8' F3 D7 R2 D' U10' D12' R3 L11 R6' L6' F11' D3 U6 R9 D3 D L7' F7 F2' F9' L4' L9' F11 R8 L8' R9' B11 F9 B6' F2' R8' z' x x R7 R6' U R6 R5' U' R7' R6 U R6' x' F' R7 R6' U R6 R5' U' R7' R6 U R6' F R7 R6' U R6 R5' U' R7' R6 U R6' z z F' U U R7 R6' U R6 R5' U' R7' R6 U R6' F z U' R7 R6' U R6 R5' U' R7' R6 U R6' z x x F R7 R6' U R6 R5' U' R7' R6 U R6' R7 R6' U R6 R5' U' R7' R6 U R6' F' z' F' U U R7 R6' U R6 R5' U' R7' R6 U R6' F z' x z F' U R7 R6' U R6 R5' U' R7' R6 U R6' F x' F F U R7 R6' U R6 R5' U' R7' R6 U R6' F U R7 R6' U R6 R5' U' R7' R6 U R6' F z z x x F F U R7 R6' U R6 R5' U' R7' R6 U R6' F R7 R6' U R6 R5' U' R7' R6 U R6' F R7 R6' U R6 R5' U' R7' R6 U R6' x' F R7 R6' U R6 R5' U' R7' R6 U R6' R7 R6' U R6 R5' U' R7' R6 U R6' F R7 R6' U R6 R5' U' R7' R6 U R6' F R7 R6' U R6 R5' U' R7' R6 U R6' F x F U R7 R6' U R6 R5' U' R7' R6 U R6' F' z x F F U L6' U' L6 U' L6' U U L6 F F z F F U U L6' U' L6 U' L6' U U L6 F F z z F F U' L6' U' L6 U' L6' U U L6 F F z x x F' U L6' U' L6 U' L6' U U L6 F z U U L6' U' L6 U' L6' U U L6 z z F F U L6' U' L6 U' L6' U U L6 F F x z F' U L6' U' L6 U' L6' U U L6 F U L6' U' L6 U' L6' U U L6 x' F U L6' U' L6 U' L6' U U L6 F F U' L6' U' L6 U' L6' U U L6 F U' L6' U' L6 U' L6' U U L6 z z x x F U L6' U' L6 U' L6' U U L6 F F U L6' U' L6 U' L6' U U L6 F x' F F U U L6' U' L6 U' L6' U U L6 F U L6' U' L6 U' L6' U U L6 F U L6' U' L6 U' L6' U U L6 x F U U L6' U' L6 U' L6' U U L6 F U' L6' U' L6 U' L6' U U L6 F F z x x F R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F F R8 R7' U R5 R4' U' R8' R7 U R5' x' F R7 R6' U R5 R4' U' R7' R6 U R5' R6 R5' U R5 R4' U' R6' R5 U R5' F U R6 R5' U R5 R4' U' R6' R5 U R5' F R8 R7' U R5 R4' U' R8' R7 U R5' R7 R6' U R5 R4' U' R7' R6 U R5' F z F R7 R6' U R5 R4' U' R7' R6 U R5' F F U R8 R7' U R5 R4' U' R8' R7 U R5' F z R8 R7' U R5 R4' U' R8' R7 U R5' U U R7 R6' U R5 R4' U' R7' R6 U R5' z F F U U R8 R7' U R5 R4' U' R8' R7 U R5' F F U R6 R5' U R5 R4' U' R6' R5 U R5' z x x F R6 R5' U R5 R4' U' R6' R5 U R5' F F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' z F R8 R7' U R5 R4' U' R8' R7 U R5' R8 R7' U R5 R4' U' R8' R7 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F F U U R8 R7' U R5 R4' U' R8' R7 U R5' U R7 R6' U R5 R4' U' R7' R6 U R5' F z z F' U U R7 R6' U R5 R4' U' R7' R6 U R5' F x z F R7 R6' U R5 R4' U' R7' R6 U R5' F R8 R7' U R5 R4' U' R8' R7 U R5' F F x' F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' F R8 R7' U R5 R4' U' R8' R7 U R5' R7 R6' U R5 R4' U' R7' R6 U R5' F z z F U R8 R7' U R5 R4' U' R8' R7 U R5' U R8 R7' U R5 R4' U' R8' R7 U R5' R7 R6' U R5 R4' U' R7' R6 U R5' R6 R5' U R5 R4' U' R6' R5 U R5' F F R7 R6' U R5 R4' U' R7' R6 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F x x F R8 R7' U R5 R4' U' R8' R7
U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F R7 R6' U R5 R4' U' R7' R6 U R5' x' F U R7 R6' U R5 R4' U' R7' R6 U R5' R6 R5' U R5 R4' U' R6' R5 U R5' F R6 R5' U R5 R4' U' R6' R5 U R5' F U U R7 R6' U R5 R4' U' R7' R6 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F U U R8 R7' U R5 R4' U' R8' R7 U R5' x F F R8 R7' U R5 R4' U' R8' R7 U R5' U' R7 R6' U R5 R4' U' R7' R6 U R5' F R8 R7' U R5 R4' U' R8' R7 U R5' U R6 R5' U R5 R4' U' R6' R5 U R5' F U U R8 R7' U R5 R4' U' R8' R7 U R5' U U R6 R5' U R5 R4' U' R6' R5 U R5' z x x F U L5' U' L5 U' L5' U U L5 F' x' F' U L5' U' L5 U' L5' U U L5 F z U U L5' U' L5 U' L5' U U L5 z z F' U' L5' U' L5 U' L5' U U L5 F z x x F U L5' U' L5 U' L5' U U L5 F' U L5' U' L5 U' L5' U U L5 z' U U L5' U' L5 U' L5' U U L5 x z F U L5' U' L5 U' L5' U U L5 F' x' F F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F z z F F U U L5' U' L5 U' L5' U U L5 F F U' L5' U' L5 U' L5' U U L5 x x U U L5' U' L5 U' L5' U U L5 x' F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 F x F' U L5' U' L5 U' L5' U U L5 F U L5' U' L5 U' L5' U U L5 z x x F R5 R4' U R4 R3' U' R5' R4 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F F R8 R7' U R4 R3' U' R8' R7 U R4' F x' F U R7 R6' U R4 R3' U' R7' R6 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' U U R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' z U' R9 R8' U R4 R3' U' R9' R8 U R4' z F U R6 R5' U R4 R3' U' R6' R5 U R4' U U R5 R4' U R4 R3' U' R5' R4 U R4' F U R6 R5' U R4 R3' U' R6' R5 U R4' F F U U R7 R6' U R4 R3' U' R7' R6 U R4' z F F U' R7 R6' U R4 R3' U' R7' R6 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F z x x F R9 R8' U R4 R3' U' R9' R8 U R4' F F U R7 R6' U R4 R3' U' R7' R6 U R4' F z F U R5 R4' U R4 R3' U' R5' R4 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' F U U R7 R6' U R4 R3' U' R7' R6 U R4' F U R9 R8' U R4 R3' U' R9' R8 U R4' R8 R7' U R4 R3' U' R8' R7 U R4' z F F U' R9 R8' U R4 R3' U' R9' R8 U R4' F R8 R7' U R4 R3' U' R8' R7 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' F R5 R4' U R4 R3' U' R5' R4 U R4' z F' U U R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' x z F F R5 R4' U R4 R3' U' R5' R4 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' x' F U R9 R8' U R4 R3' U' R9' R8 U R4' R9 R8' U R4 R3' U' R9' R8 U R4' R8 R7' U R4 R3' U' R8' R7 U R4' U U R8 R7' U R4 R3' U' R8' R7 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F U' R9 R8' U R4 R3' U' R9' R8 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' F z z F R7 R6' U R4 R3' U' R7' R6 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F U U R7 R6' U R4 R3' U' R7' R6 U R4' F x x F U R8
R7' U R4 R3' U' R8' R7 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' F R7 R6' U R4 R3' U' R7' R6 U R4' F U R8 R7' U R4 R3' U' R8' R7 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' x' F R8 R7' U R4 R3' U' R8' R7 U R4' R8 R7' U R4 R3' U' R8' R7 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' U R8 R7' U R4 R3' U' R8' R7 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' F U R9 R8' U R4 R3' U' R9' R8 U R4' R7 R6' U R4 R3' U' R7' R6 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' U' R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' x F U R7 R6' U R4 R3' U' R7' R6 U R4' R5 R4' U R4 R3' U' R5' R4 U R4' U R5 R4' U R4 R3' U' R5' R4 U R4' F R9 R8' U R4 R3' U' R9' R8 U R4' U U R8 R7' U R4 R3' U' R8' R7 U R4' U R6 R5' U R4 R3' U' R6' R5 U R4' F R6 R5' U R4 R3' U' R6' R5 U R4' F U' R9 R8' U R4 R3' U' R9' R8 U R4' R6 R5' U R4 R3' U' R6' R5 U R4' z x U L4' U' L4 U' L4' U U L4 z z F' U U L4' U' L4 U' L4' U U L4 F U' L4' U' L4 U' L4' U U L4 z z x x F U L4' U' L4 U' L4' U U L4 F' z F' U U L4' U' L4 U' L4' U U L4 F z z F F U' L4' U' L4 U' L4' U U L4 F F x z F' U L4' U' L4 U' L4' U U L4 F x' F F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F z z x x F U L4' U' L4 U' L4' U U L4 F' x' F F U U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 F U L4' U' L4 U' L4' U U L4 x F U L4' U' L4 U' L4' U U L4 F L4' U' L4 U' L4' U U L4 F F z x x F R6 R5' U R3 R2' U' R6' R5 U R3' F F R7 R6' U R3 R2' U' R7' R6 U R3' F U R7 R6' U R3 R2' U' R7' R6 U R3' x' F R9 R8' U R3 R2' U' R9' R8 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' U R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' z F R10 R9' U R3 R2' U' R10' R9 U R3' U' R5 R4' U R3 R2' U' R5' R4 U R3' F U R9 R8' U R3 R2' U' R9' R8 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' F U U R8 R7' U R3 R2' U' R8' R7 U R3' z z F U R10 R9' U R3 R2' U' R10' R9 U R3' U R9 R8' U R3 R2' U' R9' R8 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F U' R6 R5' U R3 R2' U' R6' R5 U R3' F F z x x F F R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F z F R9 R8' U R3 R2' U' R9' R8 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' F F R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' z F R10 R9' U R3 R2' U' R10' R9 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' F R4 R3' U R3 R2' U' R4' R3 U R3' F U U R8 R7' U R3 R2' U' R8' R7 U R3' z F U U R5 R4' U R3 R2' U' R5' R4 U R3' F F R9 R8' U R3 R2' U' R9' R8 U R3' U' R5 R4' U R3 R2' U' R5' R4 U R3' F U R9 R8' U R3 R2' U' R9' R8 U R3' U' R7 R6' U R3 R2' U' R7' R6 U R3' U U R4 R3' U R3 R2' U'
R4' R3 U R3' x z F U U R6 R5' U R3 R2' U' R6' R5 U R3' F R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' x' F R10 R9' U R3 R2' U' R10' R9 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' U R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' F U U R10 R9' U R3 R2' U' R10' R9 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' U R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' U R5 R4' U R3 R2' U' R5' R4 U R3' F U R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' z z F U' R10 R9' U R3 R2' U' R10' R9 U R3' U' R7 R6' U R3 R2' U' R7' R6 U R3' U' R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F' U' R8 R7' U R3 R2' U' R8' R7 U R3' x x F R9 R8' U R3 R2' U' R9' R8 U R3' F U R5 R4' U R3 R2' U' R5' R4 U R3' F R10 R9' U R3 R2' U' R10' R9 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' F R7 R6' U R3 R2' U' R7' R6 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' x' F R10 R9' U R3 R2' U' R10' R9 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' U R7 R6' U R3 R2' U' R7' R6 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' R5 R4' U R3 R2' U' R5' R4 U R3' F U U R10 R9' U R3 R2' U' R10' R9 U R3' R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F U U R10 R9' U R3 R2' U' R10' R9 U R3' U U R9 R8' U R3 R2' U' R9' R8 U R3' U U R8 R7' U R3 R2' U' R8' R7 U R3' U U R5 R4' U R3 R2' U' R5' R4 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R6 R5' U R3 R2' U' R6' R5 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' x F R10 R9' U R3 R2' U' R10' R9 U R3' R10 R9' U R3 R2' U' R10' R9 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' U R6 R5' U R3 R2' U' R6' R5 U R3' U' R5 R4' U R3 R2' U' R5' R4 U R3' U R4 R3' U R3 R2' U' R4' R3 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R7 R6' U R3 R2' U' R7' R6 U R3' U' R6 R5' U R3 R2' U' R6' R5 U R3' F U' R10 R9' U R3 R2' U' R10' R9 U R3' R9 R8' U R3 R2' U' R9' R8 U R3' R8 R7' U R3 R2' U' R8' R7 U R3' R4 R3' U R3 R2' U' R4' R3 U R3' F R9 R8' U R3 R2' U' R9' R8 U R3' z x x U L3' U' L3 U' L3' U U L3 x' F' U L3' U' L3 U' L3' U U L3 F z F' U U L3' U' L3 U' L3' U U L3 F z z F F U L3' U' L3 U' L3' U U L3 F F U L3' U' L3 U' L3' U U L3 z x x F U L3' U' L3 U' L3' U U L3 F' U L3' U' L3 U' L3' U U L3 z z U U L3' U' L3 U' L3' U U L3 z F F U U L3' U' L3 U' L3' U U L3 F F x z U L3' U' L3 U' L3' U U L3 x' F' U' L3' U' L3 U' L3' U U L3 F z z x x U L3' U' L3 U' L3' U U L3 x' F' U L3' U' L3 U' L3' U U L3 F x F U L3' U' L3 U' L3' U U L3 F' U' L3' U' L3 U' L3' U U L3 z x x F R7 R6' U R2 R' U' R7' R6 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R10 R9' U R2 R' U' R10' R9
U R2' R4 R3' U R2 R' U' R4' R3 U R2' F F x' F R11 R10' U R2 R' U' R11' R10 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' z F U R10 R9' U R2 R' U' R10' R9 U R2' F U R5 R4' U R2 R' U' R5' R4 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' F U R11 R10' U R2 R' U' R11' R10 U R2' z F U R7 R6' U R2 R' U' R7' R6 U R2' F F U R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U U R3 R2' U R2 R' U' R3' R2 U R2' z F U R8 R7' U R2 R' U' R8' R7 U R2' F U' R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F F U U R11 R10' U R2 R' U' R11' R10 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' z x x F R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' F R11 R10' U R2 R' U' R11' R10 U R2' F R9 R8' U R2 R' U' R9' R8 U R2' R8 R7' U R2 R' U' R8' R7 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R9 R8' U R2 R' U' R9' R8 U R2' z F F R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F z F U U R9 R8' U R2 R' U' R9' R8 U R2' R9 R8' U R2 R' U' R9' R8 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R7 R6' U R2 R' U' R7' R6 U R2' F U U R5 R4' U R2 R' U' R5' R4 U R2' z F U R11 R10' U R2 R' U' R11' R10 U R2' U U R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R10 R9' U R2 R' U' R10' R9 U R2' U U R8 R7' U R2 R' U' R8' R7 U R2' U U R6 R5' U R2 R' U' R6' R5 U R2' F U R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' F R5 R4' U R2 R' U' R5' R4 U R2' x z F U R9 R8' U R2 R' U' R9' R8 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' F R10 R9' U R2 R' U' R10' R9 U R2' R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' F R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' F U R6 R5' U R2 R' U' R6' R5 U R2' x' F R10 R9' U R2 R' U' R10' R9 U R2' R10 R9' U R2 R' U' R10' R9 U R2' R9 R8' U R2 R' U' R9' R8 U R2' R9 R8' U R2 R' U' R9' R8 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F R8 R7' U R2 R' U' R8' R7 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' F R11 R10' U R2 R' U' R11' R10 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' z z F R11 R10' U R2 R' U' R11' R10 U R2' U' R9 R8' U R2 R' U' R9' R8 U R2' U U R5 R4' U R2 R' U' R5' R4 U R2' F U' R10 R9' U R2 R' U' R10' R9 U
R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R11 R10' U R2 R' U' R11' R10 U R2' U U R8 R7' U R2 R' U' R8' R7 U R2' U' R4 R3' U R2 R' U' R4' R3 U R2' F U' R7 R6' U R2 R' U' R7' R6 U R2' R3 R2' U R2 R' U' R3' R2 U R2' x x F R11 R10' U R2 R' U' R11' R10 U R2' R9 R8' U R2 R' U' R9' R8 U R2' U U R8 R7' U R2 R' U' R8' R7 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U U R10 R9' U R2 R' U' R10' R9 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F x' F R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' U R10 R9' U R2 R' U' R10' R9 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' R8 R7' U R2 R' U' R8' R7 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' R6 R5' U R2 R' U' R6' R5 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U U R11 R10' U R2 R' U' R11' R10 U R2' R9 R8' U R2 R' U' R9' R8 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R6 R5' U R2 R' U' R6' R5 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' U R9 R8' U R2 R' U' R9' R8 U R2' R8 R7' U R2 R' U' R8' R7 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' R4 R3' U R2 R' U' R4' R3 U R2' U R3 R2' U R2 R' U' R3' R2 U R2' F U R7 R6' U R2 R' U' R7' R6 U R2' x F R10 R9' U R2 R' U' R10' R9 U R2' U R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' U R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U R11 R10' U R2 R' U' R11' R10 U R2' R10 R9' U R2 R' U' R10' R9 U R2' U' R8 R7' U R2 R' U' R8' R7 U R2' R7 R6' U R2 R' U' R7' R6 U R2' R6 R5' U R2 R' U' R6' R5 U R2' U' R4 R3' U R2 R' U' R4' R3 U R2' R3 R2' U R2 R' U' R3' R2 U R2' F U' R10 R9' U R2 R' U' R10' R9 U R2' R9 R8' U R2 R' U' R9' R8 U R2' U R7 R6' U R2 R' U' R7' R6 U R2' U R5 R4' U R2 R' U' R5' R4 U R2' U' R3 R2' U R2 R' U' R3' R2 U R2' F U U R9 R8' U R2 R' U' R9' R8 U R2' R6 R5' U R2 R' U' R6' R5 U R2' z x z F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 F z U' L2' U' L2 U' L2' U U L2 z F F U U L2' U' L2 U' L2' U U L2 F F z x x F' U L2' U' L2 U' L2' U U L2 F z F' U U L2' U' L2 U' L2' U U L2 F z F L2' U' L2 U' L2' U U L2 F' z x z F U L2' U' L2 U' L2' U U L2 F' U L2' U' L2 U' L2' U U L2 x' F' U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 z z F' U' L2' U' L2 U' L2' U U L2 F x x F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F F U L2' U' L2 U' L2' U U L2 x' F F U U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 F U L2' U' L2 U' L2' U U L2 x F U L2' U' L2 U' L2' U U L2 F' L2' U' L2 U' L2' U U L2 R3 R2' U U R3 R2' U U R3' R2
x' L3' L2 U U L3 L2' x' U U R3 R2' U U R3' R2 R6 R5' U U R6 R5' U U R6' R5 x' L6' L5 U U L6 L5' x' U U R6 R5' U U R6' R5 U U B L U' F U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U U B L L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U U B L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F7' F R U' R' U F7 U L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U L U R R B U' R B B z z x' F7' F R U' R' U F7 U L U U B L L U U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 F' U' x x F U L B L L R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B B U' D B D B U B U' L U B L U' L' R R B U' R B B z z x' F7' F R U' R' U F7 U L U' B U B U B U D B' L L U' B U B U B U D B B U' D B D B U B U R R B U' R B B z z x' F7' F R U' R' U F7 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B L L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z
x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B B L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U U L R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B U' L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B B U' D B D B U B B L L U R R B U' R B B z z x' F8' F R U' R' U F8 U U B L L U' B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U U B L L U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F8' F R U' R' U F8 U L U U L R R B U' R B B z z x' F8' F R U' R' U F8 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F9' F R U' R' U F9 U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U B L L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U U L U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U L R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U' L U R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B U' L U U B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B' L L U' R R B U' R B B z z x' F9' F R U' R' U F9 U L U' B U B U B
U D B' L L B L U' L' R R B U' R B B z z x' F9' F R U' R' U F9 F' U' B L U' F U L U' B U B U B U D B' L L R R B U' R B B z z x' F9' F R U' R' U F9 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' B L L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U B L L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L R R B U' R B B z z x' F10' F R U' R' U F10 U L U' L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U B L L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B L U' B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U' D B D B U B U' L R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' L U U B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U B L L B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B' L L R R B U' R B B z z x' F10' F R U' R' U F10 U L U B L L B L U' L' R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U L U R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F10' F R U' R' U F10 U L U' B U B U B U D B' L L U' R R B U' R B B z z x' F10' F R U' R' U F10 F' U U B L U' F U L U' B U B U B U D B B U' D B D B U B B L L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B U B U B U D B B U U B L L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B U B U B U D B B U' L U U B L U' L' R R B U' R B B z z x' F11' F R U' R' U F11 U L R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B B L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B B U' D B D B U B U U L R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B' L L R R B U' R B B z z x' F11' F R U' R' U F11 U L U B L L R R B U' R B B z z x' F11' F R U' R' U F11
F' U U B L U' F U L U' B U B U B U D B B U U L R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B B U' D B D B U B U U L U' R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B U B U B U D B B U B L L R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' B L L U' R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B B U B L L U R R B U' R B B z z x' F11' F R U' R' U F11 F' U U B L U' F U L U' L U U R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B B U' D B D B U B U' L U' B U B U B U D B' L U' L' R R B U' R B B z z x' F11' F R U' R' U F11 U L U' B U B U B U D B' L L U U B L U' L' R R B U' R B B z z x' F11' F R U' R' U F11 F' z' x D F' U' R U D' U' F F U z z F' U U z F' U' D' U U y' F R F' y D U U D D U' y' F R' F' y D D y' F R F' y D U D' U y' F R F' y D U' D D R' D R D D y' F R F' y U y' F R F' y U' y y L' U L U F U' F' U' R U' R' U' F' U F U' z U' z U' z U' U' L' U L U F U' F' z z U R U' R' U' F' U F z' U' z R U' R' U' F' U F U' F R U R' U' R U R' U' F' U R' U' R U' R' U U R F U F' U F U U F' U R R F F R' B' R F F R' B R' U R' U' R U' R' U U R z z F U F' U F U U F' U U R' U' R U' R' U U R z z F U F' U F U U F' U U D'
八 - 十三阶魔方&spm=1001.2101.3001.5002&articleId=164019557&d=1&t=3&u=dfc08123bdb746b78a0cbf952b781da7)
1037

被折叠的 条评论
为什么被折叠?



