Cuber is a Rubik’s Cube simulator.
Note: This repository is merely an archive of my original Rubik’s Cube demo, interred here for historical purposes—to document my initial conceptual approach and to exemplify my insane flair for code comments, complete with ASCII art illustrations. Cuber went on to power the Rubik’s Cube Google Doodle, the Chrome Cube Lab experiments, and my own Rubik’s Cube Explorer. It was also used to create the branding for the Liberty Science Center Beyond Rubik’s Cube exhibition. For more information see Stewdio.
From the desktop
Simply drop the
index.html file onto a Chrome browser window.
From an ad-hoc server
From the command line type the following to create a server, where
(the default) is an optional port number argument:
python -m SimpleHTTPServer 8000
/.htaccess is configured to require a valid login which you will
need to configure on your own server. Alternatively this file can be removed
from the package entirely.
Separation of state and visuals
I wanted to keep the Cube’s internal state entirely separate and independent of the visual rendering as a way to hopefully allow for future ports to serve new and unforeseen purposes. Sometimes it makes for what seems like redundancy but I think in the long term it’ll be worth it.
I’ve tried to break the problem down into what amounts to Classes. I’m a big fan of Prototypal Inheritance but I’m hoping this approach—including separating these Classes into separate files—makes things clear and easy. Also it’s pretty awesome when you’re inspecting things via the console and the console can actually tell you that this particular Array holds Cubelets, for example. The bulk of the fun located in
/scripts/models/. The app is
Global scope and the Console
Through code comments I’ve tried very hard to be as clear as possible about what I’m doing and why I’m doing it. This is only partially altruism. It’s perhaps primarily so I can have some chance of understanding my own code at some future date. Ever write an app and come back to it six months later? Yea. It’s a problem. That’s why I comment so heavily.
I’ve endeavored to make the Cube highly inspectable from both third-party scripts and by humans using the browser’s console. To get a quick sense of what’s possible try out each of the following commands. (It’s fun!)
cube.inspect() cube.inspect( true ) cube.front cube.front.northWest.inspect() cube.front.northWest.up.color.name
To understand the basic vocabulary and mapping of the Cube and Cubelets see
the comments at the head of
/scripts folder. (Also, if your browser does not support CSS styling
within the console you’re going to have a bad time.)
It’s also easy to alter properties of the Cube like so:
cube.standing.setOpacity( 0.1 ) cube.corners.setRadius( 90 ) cube.corners.getRadius( 90 )
These functions are chainable. And really, who doesn’t love chaining?
If you’re hungry for more check out
groups.js to see all the goodies.
groups.js you’ll also spot some filtering functions:
cube.hasColors( RED, BLUE ).showIds() cube.hasId( 0 )// A Cubelet’s ID never changes. cube.hasAddress( 0 )// But its address changes when it moves. cube.hasProperty( 'address', 0 )// Equivalent to previous line.
And guess what—these are also chainable. (I know, right?!)
cube.hasColors( RED, BLUE ).hasType( 'corner' ).setRadius( 90 )
The infrastructure for writing solvers is more or less there. Calling
cube.solve() will set
cube.isSolving = true and then with each run through
cube.loop() the selected solver will be asked to assess the Cube. I’ve
provided the bare beginnings of a simple layer-by-layer solver. Sadly, I only
got as far as solving the Top Cross but hopefully the solver’s code comments
and verbose console output can serve as a primer for you to write your own!
Lagniappe: While writing the solver it seemed really helpful to fully define
the idea of orientation / direction so I wrote a Class and some STATICs
that make comparing directions a little easier. (See
FRONT.getOpposite() === BACK FRONT.neighbors FRONT.getClockwise() FRONT.getClockwise( 2 )
The spacial mapping used here is defined at the head of
Whatever face you’re looking at has an inherrent ‘up’ and from that reference
point you can ask what face we’d be looking at if we rotated clockwise by a
90 degree twist, for example.
FRONT.getUp() === UP FRONT.getClockwise() === RIGHT UP.getUp() === BACK UP.getRotation( -1, LEFT )
It gets weird pretty quickly but once you’re in the correct headspace these functions become incredibly useful. Why do all this rather than hardcore matrix math? Because 42.
I use tabs, not spaces for codeblock indentation—functions, object blocks, and so on. Tab is the only true unit of indentation. My tabs are the width of four spaces. Two is too small. Eight is just too damn wide. Within lines of code I’ll often add bits of whitespace to line up consecutive equal signs or other recurring symbols if it’s subtle and not too wonky:
right = 2 left = 3
I’ll often add a space between an open-parenthesis and a token; same for between a token and close-parenthesis. Line breaks after an open bracket are also desirable.
Two line breaks before line comments. One line break after them. Two spaces following a comment’s slashes. (One for hanging quotation marks in order to optically align the text itself.) For critical to-do’s I add two juxtaposed ampersats to both visually separate them and make them easy to search for. Just a quirk of mine. Occasionally if the situation calls for it I’ll use special “rhombus” commnents for big visual breaks. They have three horizontal spaces between slashes and text and vertical padding inside like so:
///////////////// // // // Rhombus // // // /////////////////
I pad these rhombus comments with four line breaks above and two below.
Occasionally stabbed at in 2013