var state = []; var rotateIdxs_old = null; var rotateIdxs_new = null; var stateToFE = null; var FEToState = null; var legalMoves = null; var solveStartState = []; var solveMoves = []; var solveMoves_rev = []; var solveIdx = null; var solution_text = null; // var faceNames = ["top", "bottom", "left", "right", "back", "front"]; var faceNames = ["top", "right", "front", "bottom", "left", "back"]; var colorMap = { 0: "#ffffff", // 白色 1: "#ff0000", // 红色 2: "#00cc00", // 绿色 3: "#ffff00", // 黄色 4: "#ff9900", // 橙色 5: "#0000ff" // 蓝色 }; var lastMouseX = 0, lastMouseY = 0; var rotX = -30, rotY = -30; var moves = [] var initState = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5 ]; // 定义 idx 对换映射表 const idxSwapMap = { 6: 0, 0: 6, 7: 1, 1: 7, 8: 2, 2: 8, 27: 33, 33: 27, 28: 34, 34: 28, 29: 35, 35: 29 }; function mapIndex(idx) { return (idx in idxSwapMap) ? idxSwapMap[idx] : idx; } function reOrderArray(arr,indecies) { var temp = [] for(var i = 0; i < indecies.length; i++) { var index = indecies[i] temp.push(arr[index]) } return temp; } /* Rand int between min (inclusive) and max (exclusive) */ function randInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function clearCube() { for (i = 0; i < faceNames.length; i++) { var myNode = document.getElementById(faceNames[i]); while (myNode.firstChild) { myNode.removeChild(myNode.firstChild); } } } function restoreCube() { setStickerColors(initState) } // function setStickerColors(newState) { // state = newState // clearCube() // idx = 0 // for (i = 0; i < faceNames.length; i++) { // for (j = 0; j < 9; j++) { // var iDiv = document.createElement('div'); // iDiv.className = 'sticker'; // // 修正颜色索引获取方式 // iDiv.style["background-color"] = colorMap[newState[idx]] // document.getElementById(faceNames[i]).appendChild(iDiv); // idx = idx + 1 // } // } // } function setStickerColors(newState) { state = newState; clearCube(); idx = 0; for (i = 0; i < faceNames.length; i++) { for (j = 0; j < 9; j++) { var iDiv = document.createElement('div'); iDiv.className = 'sticker'; swaped_idx = mapIndex(idx) // 设置颜色 iDiv.style["background-color"] = colorMap[newState[swaped_idx]]; // 在 sticker 上显示数字(idx) iDiv.textContent = swaped_idx; // 显示在小方块里面 iDiv.style.color = "black"; // 文字颜色 iDiv.style.fontSize = "14px"; // 字体大小 iDiv.style.textAlign = "center"; // 居中 iDiv.style.lineHeight = "33.3%"; // 垂直居中 document.getElementById(faceNames[i]).appendChild(iDiv); idx = idx + 1; } } } function buttonPressed(ev) { var face = '' var direction = '' if (ev.shiftKey) { direction = '_inv' } if (ev.which == 85 || ev.which == 117) { face='U' } else if (ev.which == 68 || ev.which == 100) { face = 'D' } else if (ev.which == 76 || ev.which == 108) { face = 'L' } else if (ev.which == 82 || ev.which == 114) { face = 'R' } else if (ev.which == 66 || ev.which == 98) { face = 'B' } else if (ev.which == 70 || ev.which == 102) { face = 'F' } if (face != '') { clearSoln(); moves.push(face + direction); nextState(); } } function enableScroll() { document.getElementById("first_state").disabled=false; document.getElementById("prev_state").disabled=false; document.getElementById("next_state").disabled=false; document.getElementById("last_state").disabled=false; } function disableScroll() { document.getElementById("first_state").blur(); //so keyboard input can work without having to click away from disabled button document.getElementById("prev_state").blur(); document.getElementById("next_state").blur(); document.getElementById("last_state").blur(); document.getElementById("first_state").disabled=true; document.getElementById("prev_state").disabled=true; document.getElementById("next_state").disabled=true; document.getElementById("last_state").disabled=true; } /* Clears solution as well as disables scroll */ function clearSoln() { solveIdx = 0; solveStartState = []; solveMoves = []; solveMoves_rev = []; solution_text = null; document.getElementById("solution_text").innerHTML = "Solution:"; disableScroll(); } function setSolnText(setColor=true) { solution_text_mod = JSON.parse(JSON.stringify(solution_text)) if (solveIdx >= 0) { if (setColor == true) { solution_text_mod[solveIdx] = solution_text_mod[solveIdx].bold().fontcolor("blue") } else { solution_text_mod[solveIdx] = solution_text_mod[solveIdx] } } document.getElementById("solution_text").innerHTML = "Solution: "+ solution_text_mod.join(" "); } function enableInput() { document.getElementById("scramble").disabled=false; document.getElementById("solve").disabled=false; document.getElementById("clear").disabled=false; $(document).on("keypress", buttonPressed); } function disableInput() { document.getElementById("scramble").disabled=true; document.getElementById("solve").disabled=true; $(document).off("keypress", buttonPressed); } function nextState(moveTimeout=0) { if (moves.length > 0) { disableInput(); disableScroll(); move = moves.shift() // get Move // 添加安全检查 if (!rotateIdxs_new || !rotateIdxs_new[move]) { console.error('Invalid move or rotateIdxs_new not initialized:', move); enableInput(); return; } //convert to python representation state_rep = reOrderArray(state,FEToState) newState_rep = JSON.parse(JSON.stringify(state_rep)) //swap stickers for (var i = 0; i < rotateIdxs_new[move].length; i++) { newState_rep[rotateIdxs_new[move][i]] = state_rep[rotateIdxs_old[move][i]] } // Change move highlight if (moveTimeout != 0){ //check if nextState is used for first_state click, prev_state,etc. solveIdx++ setSolnText(setColor=true) } //convert back to HTML representation newState = reOrderArray(newState_rep,stateToFE) //set new state setStickerColors(newState) //Call again if there are more moves if (moves.length > 0) { setTimeout(function(){nextState(moveTimeout)}, moveTimeout); } else { enableInput(); if (solveMoves.length > 0) { enableScroll(); setSolnText(); } } } else { enableInput(); if (solveMoves.length > 0) { enableScroll(); setSolnText(); } } } function scrambleCube() { disableInput(); clearSoln(); numMoves = randInt(100,200); for (var i = 0; i < numMoves; i++) { moves.push(legalMoves[randInt(0,legalMoves.length)]); } nextState(0); } function solveCube() { disableInput(); clearSoln(); document.getElementById("solution_text").innerHTML = "SOLVING..." $.ajax({ url: '/solve', data: JSON.stringify({"state": state}), type: 'POST', contentType: 'application/json', dataType: 'json', // timeout: 5000, success: function(response) { if (response.error) { // 处理业务逻辑错误 document.getElementById("solution_text").innerHTML = "Error: " + response.error; enableInput(); } else { // 正常处理成功响应 solveStartState = JSON.parse(JSON.stringify(state)) solveMoves = response["moves"]; solveMoves_rev = response["moves_rev"]; solution_text = response["solve_text"]; solution_text.push("SOLVED!") setSolnText(true); moves = JSON.parse(JSON.stringify(solveMoves)) setTimeout(function(){nextState(500)}, 500); } }, error: function(xhr, status, error) { // 处理HTTP请求错误 console.log("AJAX Error:", status, error); var errorMessage = "请求失败,请重试"; if (status === "timeout") { errorMessage = "请求超时,请重试"; } else if (xhr.status === 404) { errorMessage = "未找到解决方案"; } else if (xhr.status === 500) { errorMessage = "服务器内部错误,请稍后再试"; } else if (xhr.status === 400) { errorMessage = "请求参数错误"; } document.getElementById("solution_text").innerHTML = errorMessage; enableInput(); } }); } $( document ).ready($(function() { disableInput(); clearSoln(); $.ajax({ url: '/initState', data: {}, type: 'POST', dataType: 'json', success: function(response) { setStickerColors(response["state"]); rotateIdxs_old = response["rotateIdxs_old"]; rotateIdxs_new = response["rotateIdxs_new"]; stateToFE = response["stateToFE"]; FEToState = response["FEToState"]; legalMoves = response["legalMoves"] enableInput(); }, error: function(error) { console.log(error); }, }); $("#cube").css("transform", "translateZ( -100px) rotateX( " + rotX + "deg) rotateY(" + rotY + "deg)"); //Initial orientation $('#scramble').click(function() { scrambleCube() }); $('#solve').click(function() { solveCube() }); $('#clear').click(function() { restoreCube() }) $('#first_state').click(function() { if (solveIdx > 0) { moves = solveMoves_rev.slice(0, solveIdx).reverse(); solveIdx = 0; nextState(); } }); $('#prev_state').click(function() { if (solveIdx > 0) { solveIdx = solveIdx - 1 moves.push(solveMoves_rev[solveIdx]) nextState() } }); $('#next_state').click(function() { if (solveIdx < solveMoves.length) { moves.push(solveMoves[solveIdx]) solveIdx = solveIdx + 1 nextState() } }); $('#last_state').click(function() { if (solveIdx < solveMoves.length) { moves = solveMoves.slice(solveIdx, solveMoves.length); solveIdx = solveMoves.length nextState(); } }); $('#cube_div').on("mousedown", function(ev) { lastMouseX = ev.clientX; lastMouseY = ev.clientY; $('#cube_div').on("mousemove", mouseMoved); }); $('#cube_div').on("mouseup", function() { $('#cube_div').off("mousemove", mouseMoved); }); $('#cube_div').on("mouseleave", function() { $('#cube_div').off("mousemove", mouseMoved); }); console.log( "ready!" ); })); function mouseMoved(ev) { var deltaX = ev.pageX - lastMouseX; var deltaY = ev.pageY - lastMouseY; lastMouseX = ev.pageX; lastMouseY = ev.pageY; rotY += deltaX * 0.2; rotX -= deltaY * 0.5; $("#cube").css("transform", "translateZ( -100px) rotateX( " + rotX + "deg) rotateY(" + rotY + "deg)"); }