==============================================================================
Cuber is a Rubik’s Cube simulator.
I basically took the awesome Cuber Demo from @stewdio and added the possibility of connecting a Smart Rubik Cube (a 3x3x3 cube with bluetooth feature).
I am a speedcuber, and this project is merely a coding/cubing practice for me! I am practicing my coding skills by adding features to turn this project into a speedcubing practice platform; at the same time that I have an excuse to play with my Giiker Cube. :) If you don't know what speedcubing is, Netflix have such a great documentary about it!
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 8000
(the default) is an optional port number argument:
python -m SimpleHTTPServer 8000
From Apache
The file /.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.
Modularity 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 controlled by /scripts/models/erno.js
Global scope and the Console Lambda functions are great. And namespacing components into wrapper objects can be seriously useful. But I’ve purposely (and carefully) placed a lot of important bits right into the main scope so that tinkerers may pop open Chrome’s JavaScript Console and start poking around. This also makes it easy to write bookmarklets or other hacks.
Documentation
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.
Inspection 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 cube.js
, slices.js
, and cubelets.js
within the /scripts
folder. (Also, if your browser does not support CSS styling within the console you’re going to have a bad time.)
Manipulation
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?
cube.hidePlastics().hideStickers().showWireframes().showIds()
If you’re hungry for more check out groups.js
to see all the goodies.
Search
Lurking around 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 )
Solving
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 directions.js
for details.)
FRONT.getOpposite() === BACK
FRONT.neighbors
FRONT.getClockwise()
FRONT.getClockwise( 2 )
The spacial mapping used here is defined at the head of cubelets.js
.
Whatever face you’re looking at has an inherent ‘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.
Semicolons
First, I only use semicolons when absolutely necessary. By definition any valid JavaScript interpreter must perform automatic semicolon insertion.
This means you just don’t need them. Let me repeat: YOU JUST DON’T NEED THEM.
Leaving them out makes your code typographically cleaner; reduces visual clutter and noise in the signal. It’s good for your soul.
Whitespace
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.
Comments
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.
Stewart Smith Occasionally stabbed at in 2013