Solves a rubiks cube.
Created
February 20, 2019 13:43
-
-
Save redbullmarky/b710809b4f4589777a09d7daf5a0c5c4 to your computer and use it in GitHub Desktop.
Rubiks cube solver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <body onload="APP=APP(); APP.main() " | |
| onresize="APP.setCanvasSize()" > | |
| <h2>Rubik’s Cube.</h2> | |
| <div id="left"> | |
| <!-- <canvas id="canvas" width="600" height="600"> --> | |
| <canvas id="canvas" style="width: 100%;" > | |
| Oops, your browser does not support the canvas element. | |
| </canvas> | |
| </div> | |
| <div id="right"> | |
| <h3>Controls: </h3> | |
| <p> | |
| Rubik’s cube can run in two modes: | |
| </p> | |
| <ul> | |
| <li><b>Random:</b> Make 10 random moves, then “ | |
| solve” the randomized cube by reversing them.</li> | |
| <li><b>Manual:</b> Make moves by hand using three controls: | |
| <ul> | |
| <li>Axis: Sets x, y or z to the axis of rotation</li> | |
| <li>Plane: Sets -1, 0, 1 as the coordinate of the move’s plane along the axis.</li> | |
| <li>Rotation: Either + or - 90 degrees</li> | |
| </ul> | |
| </li> | |
| </ul> | |
| <hr> | |
| <button name="pause" value="Pause" onclick="APP.setPause(this.value)">Pause</button> | |
| <button name="demo" value="Manual" onclick="APP.setDemo(this.value);APP.setPause()">Manual</button> | |
| <select name="speed" onchange="APP.setSpeed(this.value)"> | |
| <option value = "240" selected>Slow</option> | |
| <option value = "120">Medium</option> | |
| <option value = "60">Fast</option> | |
| </select> | |
| <br /><br /> | |
| <form> | |
| <table> | |
| <thead><b>Manual Controls | |
| <i class='small'>(click 'Manual' again to return to demo mode)</i> | |
| </b></thead> | |
| <tr> | |
| <td>Axis:</td> | |
| <td> | |
| <input type="radio" id='x' name="axis" value="x" | |
| onchange="APP.setAxis(0)" checked="checked" /> | |
| <label for="x">x</label> | |
| </td> | |
| <td> | |
| <input type="radio" id='y' name="axis" value="y" | |
| onchange="APP.setAxis(1)" /> | |
| <label for="y">y</label> | |
| </td> | |
| <td> | |
| <input type="radio" id='z' name="axis" value="z" | |
| onchange="APP.setAxis(2)" /> | |
| <label for="z">z</label> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Plane:</td> | |
| <td> | |
| <input type="radio" id='-1' name="plane" value="-1" | |
| onchange="APP.setPlane(-1)" /> | |
| <label for="-1">-1</label> | |
| </td> | |
| <td> | |
| <input type="radio" id='0' name="plane" value="0" | |
| onchange="APP.setPlane(0)" /> | |
| <label for="0">0</label> | |
| </td> | |
| <td> | |
| <input type="radio" id='+1' name="plane" value="+1" | |
| onchange="APP.setPlane(1)" checked="checked"/> | |
| <label for="+1">+1</label> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Rotation:</td> | |
| <td> | |
| <input type="radio" id='-1'name="rotation" value="-90" | |
| onchange="APP.setRotation(-90)" /> | |
| <label for="-1">-1</label> | |
| </td> | |
| <td> | |
| <input type="radio" id='+1' name="rotation" value="+90" | |
| onchange="APP.setRotation(+90)" checked="checked"/> | |
| <label for="+1">+1</label> | |
| </td> | |
| </tr> | |
| </table> | |
| </form> | |
| <button name="Move" value="Move" onclick="APP.setDoMove()">Move</button> | |
| <button name="Random" value="Random" onclick="APP.setDoRandomMove()">Random</button> | |
| <button name="Solve" value="Solve" onclick="APP.setSolve()">Solve</button> | |
| <button name="Reset" value="Reset" onclick="APP.resetView()">Reset</button> | |
| <ul> | |
| <li>Rotate: Click / Drag </li> | |
| <li>Reset: To Initial View</li> | |
| <li>Resize: Cube Is Responsive!</li> | |
| </ul> | |
| </div> | |
| </body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| APP=function() { | |
| var gl = glx.initGL("canvas", 1), | |
| program = glx.initShaders(gl, "vertexShader", "fragmentShader"), | |
| vPosition = gl.getAttribLocation(program, "vPosition"), | |
| vColor = gl.getAttribLocation(program, "vColor"), | |
| cube = glx.createCube(.49, // rubik's colors: | |
| [ [1,1,1], [1,0,0], [0,0,1], [1,.5,0], [0,1,0], [1,1,0] ]), | |
| moves = [], | |
| move, | |
| matrices = [], | |
| solving = false, | |
| demo = true, | |
| uiFramesPerMove = 120, | |
| uiDoMove = false, | |
| uiRandomMove = false, | |
| uiMove = [ [1,0,0], +1, +90 ], | |
| uiEye = [10,5,10]; | |
| function init() { | |
| var typedArray, | |
| cubeVertexBuf = gl.createBuffer(), | |
| cubeColorBuf = gl.createBuffer(), | |
| cubeIndexBuf = gl.createBuffer(); | |
| typedArray = new Float32Array(cube.vertices); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuf); | |
| gl.bufferData(gl.ARRAY_BUFFER, typedArray, gl.STATIC_DRAW); | |
| gl.enableVertexAttribArray(vPosition); | |
| gl.vertexAttribPointer(vPosition, cube.cols, gl.FLOAT, false, 0, 0); | |
| typedArray = new Float32Array(cube.colors); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, cubeColorBuf); | |
| gl.bufferData(gl.ARRAY_BUFFER, typedArray, gl.STATIC_DRAW); | |
| gl.enableVertexAttribArray(vColor); | |
| gl.vertexAttribPointer(vColor, cube.colorcols, gl.FLOAT, false, 0, 0); | |
| typedArray = new Uint16Array(cube.indices); | |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeIndexBuf); | |
| gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, typedArray, gl.STATIC_DRAW); | |
| initMatrices(); | |
| } | |
| function initMatrices () { | |
| matrices = []; | |
| moves = []; | |
| for (var x=-1; x <= 1; x++) { | |
| for (var y=-1; y <= 1; y++) { | |
| for (var z=-1; z <= 1; z++) { | |
| matrices.push( | |
| mat4.translate( | |
| mat4.identity(mat4.create()), | |
| [x, y, z])); | |
| } | |
| } | |
| } | |
| } | |
| function createRandomMove() { | |
| var iaxis = glx.randomInt(3), | |
| axis = [0,0,0], | |
| plane = glx.randomFromTo(-1,1), | |
| rot = 90*(2*glx.randomInt(2) - 1); | |
| axis[iaxis] = 1; | |
| return createForwardMove(axis, plane, rot); | |
| } | |
| function createReverseMove() { | |
| var move = moves.pop(); | |
| move[2] = - move[2]; | |
| return createMove(move[0],move[1],move[2],move[3]); | |
| } | |
| function createForwardMove(axis, plane, rot) { | |
| moves.push([axis,plane,rot]); | |
| return createMove(axis, plane, rot); | |
| } | |
| function createMove(axis, plane, rot) { | |
| var imat = axis.indexOf(1)+12, | |
| incr = glx.degToRad(rot) / uiFramesPerMove, | |
| stopframe = glx.animFrame + uiFramesPerMove; | |
| function done () { | |
| return glx.animFrame > stopframe; | |
| } | |
| function next(mat) { | |
| if ( !done() && (mat[imat] == plane) ) { | |
| var rmat = mat4.identity(mat4.create()); | |
| mat4.rotate(rmat, incr, axis); | |
| mat4.multiply(rmat, mat, mat); | |
| if (glx.animFrame == stopframe) | |
| glx.vmod(mat, Math.round); | |
| } | |
| } | |
| return { | |
| done: done, | |
| next: next | |
| }; | |
| } | |
| function setUniformMatrix(uniformName, matrix) { | |
| gl.uniformMatrix4fv( | |
| gl.getUniformLocation(program, uniformName), false, matrix); | |
| } | |
| function checkMove () { | |
| if( !move || move.done() ) { | |
| if (demo) { | |
| solving = (solving && moves.length != 0) || | |
| (!solving && moves.length == 10); | |
| if (solving) | |
| move=createReverseMove(); | |
| else | |
| move=createRandomMove(); | |
| } else { // manual | |
| if (uiDoMove) { | |
| if (uiRandomMove) | |
| move=createRandomMove(); | |
| else | |
| move = createForwardMove(uiMove[0], uiMove[1], uiMove[2]); | |
| uiDoMove = false; | |
| uiRandomMove = false; | |
| } else if (solving && moves.length != 0) { | |
| move=createReverseMove(); | |
| } else { | |
| if (move && move.done()) { | |
| //setPause(true); | |
| glx.animPause = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| function display() { | |
| checkMove(); | |
| doDisplay(true); | |
| } | |
| function redisplay() { | |
| doDisplay(false); | |
| } | |
| function doDisplay(checkMove) { | |
| var pMatrix = mat4.perspective(45, glx.aspectRatio(gl), 0.1, 100.0), | |
| laMatrix = mat4.lookAt(uiEye, [0,0,0], [0,1,0]); | |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
| setUniformMatrix("pMatrix", pMatrix); | |
| setUniformMatrix("laMatrix", laMatrix); | |
| for (var i=0; i < matrices.length; i++) { | |
| if(checkMove && move) move.next(matrices[i]); | |
| setUniformMatrix("mvMatrix", matrices[i]); | |
| gl.drawElements(gl.TRIANGLES, cube.indices.length, | |
| gl.UNSIGNED_SHORT, 0); | |
| }; | |
| } | |
| function main() { | |
| init(); | |
| gl.clearColor(0.0, 0.0, 0.0, 1.0); | |
| gl.enable(gl.DEPTH_TEST); | |
| glx.animate(display); | |
| } | |
| // UI: enclose all UI within a closure-module | |
| var uiStartX, | |
| uiStartA, | |
| uiR; | |
| gl.canvas.addEventListener("mousedown", mouseDown, false); | |
| gl.canvas.addEventListener("mouseup", mouseUp, false); | |
| function mouseDown(e) { | |
| uiStartX = e.offsetX; | |
| uiR = Math.sqrt(10*10 + 10*10); | |
| uiStartA = glx.radToDeg(Math.acos(uiEye[0]/uiR)); | |
| gl.canvas.addEventListener("mousemove", mouseDrag, false); | |
| } | |
| function mouseUp(e) { | |
| gl.canvas.removeEventListener("mousemove", mouseDrag, false); | |
| } | |
| function mouseDrag(e) { | |
| var dx = e.offsetX - uiStartX, | |
| da = Math.round(360*(dx/gl.canvas.height)); | |
| console.log("mouseDrag: dx: "+dx+" r: "+uiR+" a: "+uiStartA+" da: "+da); | |
| uiEye[0] = uiR*Math.cos(glx.degToRad(uiStartA+da)); | |
| uiEye[2] = uiR*Math.sin(glx.degToRad(uiStartA+da)); | |
| if (glx.animPause) redisplay(); | |
| } | |
| function setCanvasSize() { | |
| glx.resizeCanvas(gl, 1); | |
| if (glx.animPause) redisplay(); | |
| } | |
| function uiStr() { | |
| return "[" + vec3.str(uiMove[0]) + ", " + uiMove[1] + ", " + uiMove[2]+ "]" | |
| } | |
| function setAxis(i) { | |
| var uiAxis = [0,0,0]; | |
| uiAxis[i] = 1; | |
| uiMove[0] = uiAxis; | |
| console.log("setAxis: "+vec3.str(uiAxis)+" "+ uiStr()); | |
| } | |
| function setPlane(i) { | |
| uiMove[1] = i; | |
| console.log("setPlane: "+i+" "+ uiStr()); | |
| } | |
| function setRotation(i) { | |
| uiMove[2] = i; | |
| console.log("setRotation: "+i+" "+ uiStr()); | |
| } | |
| function setDoMove() { | |
| uiDoMove = true; | |
| uiRandomMove = false; | |
| solving = false; | |
| console.log("setDoMove"); | |
| glx.animPause = false; | |
| } | |
| function setDoRandomMove() { | |
| uiDoMove = true; | |
| uiRandomMove = true; | |
| solving = false; | |
| console.log("setDoRandomMove"); | |
| glx.animPause = false; | |
| } | |
| function setPause(s) { | |
| var names = ["Pause","Resume"]; | |
| s = s?s:names[1]; // default to switching back to Pause | |
| glx.animPause=(s==names[0]); | |
| console.log("setPause: "+glx.animPause); | |
| document.getElementsByName("pause")[0].value= | |
| (glx.animPause)?names[1]:names[0]; | |
| } | |
| function setDemo(s) { | |
| var names = ["Demo","Manual"]; | |
| demo=(s==names[0]); | |
| document.getElementsByName("demo")[0].value= | |
| (demo)?names[1]:names[0]; | |
| initMatrices(); | |
| move = null; | |
| solving = false; | |
| glx.animPause = !demo; | |
| if (glx.animPause) redisplay(); | |
| console.log("setDemo: "+demo+" pause:"+glx.animPause); | |
| } | |
| function setSpeed(s) { | |
| s = s ? s : document.getElementsByName("speed")[0].value; | |
| uiFramesPerMove = parseInt(s,10); | |
| console.log("setSpeed: "+uiFramesPerMove); | |
| } | |
| function setSolve() { | |
| solving = true; | |
| uiDoMove = false; | |
| glx.animPause = false; | |
| } | |
| function resetView() { | |
| uiEye = [10,5,10]; | |
| if (glx.animPause) redisplay(); | |
| } | |
| return { | |
| // dbg | |
| dbg: function () { | |
| window.gl = gl; | |
| window.program = program; | |
| window.cube = cube; | |
| window.can = gl.canvas; | |
| window.vp = gl.getParameter( gl.VIEWPORT ); | |
| window.matrices = matrices; | |
| window.axis = document.getElementsByName("axis"); | |
| window.plane = document.getElementsByName("plane"); | |
| window.rotation = document.getElementsByName("rotation"); | |
| }, | |
| // interface | |
| main: main, | |
| setAxis: setAxis, | |
| setPlane: setPlane, | |
| setRotation: setRotation, | |
| setCanvasSize: setCanvasSize, | |
| setPause: setPause, | |
| setDemo: setDemo, | |
| setSpeed: setSpeed, | |
| setDoMove: setDoMove, | |
| resetView: resetView, | |
| setDoRandomMove: setDoRandomMove, | |
| setSolve: setSolve | |
| }; | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/131045/glMatrix.js"></script> | |
| <script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/131045/glx.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| body{ | |
| font-size:1.5em; | |
| background:#2980b9; | |
| color:#2c3e50; | |
| } | |
| body:before { | |
| content: ""; | |
| position: fixed; | |
| top: -10px; | |
| left: 0; | |
| width: 100%; | |
| height: 10px; | |
| -webkit-box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| -moz-box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| z-index: 100; | |
| } | |
| h2{ | |
| text-align:center; | |
| padding:1em; | |
| } | |
| #left { | |
| float: left; | |
| width: 50%; | |
| margin: 0; | |
| padding: 0; | |
| -webkit-box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| -moz-box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| box-shadow: 0px 0px 10px rgba(0,0,0,.8); | |
| } | |
| #right { | |
| float: right; | |
| width: 47%; | |
| margin: 0; | |
| padding: 0 0 0 10px; | |
| border-left: 1px dashed #000; | |
| } | |
| #right h3 { | |
| margin-top: 0; | |
| padding-top: 0; | |
| } | |
| hr{ | |
| border: 1px dashed #000; | |
| } | |
| button{ | |
| border:none; | |
| width:8vw; | |
| padding:.5em; | |
| margin:1%; | |
| font-size:1rem; | |
| color:#fff; | |
| background:#c0392b; | |
| box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| -webkit-box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| -moz-box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| } | |
| select{ | |
| color:#fff; | |
| background:#95a5a6; | |
| padding:.5em; | |
| margin:1%; | |
| font-size:1rem; | |
| border:none; | |
| width:20%; | |
| box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| -webkit-box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| -moz-box-shadow:1px 1px 1px 1px rgba(0,0,0,0.4); | |
| } | |
| input[type=radio] { | |
| border: 0px; | |
| height:1.5vh; | |
| width:50%; | |
| } | |
| label{ | |
| display:inline-block; | |
| font-weight:bold; | |
| } | |
| table{ | |
| width:60%; | |
| } | |
| tr, td{ | |
| font-size:1rem; | |
| padding-bottom:1em; | |
| margin-bottom:2%; | |
| } | |
| thead{ | |
| text-align:center; | |
| } | |
| .small{ | |
| font-size:.5rem; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment