Rubik’s cube #
Introduction #
The Rubik’s cube is a 3x3x3 cube with 6 faces. Each face has 9 stickers, each sticker has a color. The cube can be rotated in 3 dimensions. The goal is to rotate the cube so that each face has the same color.
CODE
const WIDTH = 500, HEIGHT = 500;
const SIZE = WIDTH / 15;
const MOVES = [
"U", "U'", "U2",
"D", "D'", "D2",
"R", "R'", "R2",
"L", "L'", "L2",
"F", "F'", "F2",
"B", "B'", "B2",
];
const COLOR_MAP = {
1: [255],
2: [255, 255, 0],
3: [255, 0, 0],
4: [255, 128, 0],
5: [0, 255, 0],
6: [0, 0, 255],
};
const KEY_MAP = {
U: () => turnFace(up, 0),
"U'": () => { turnFace(up, 0); turnFace(up, 0); turnFace(up, 0); },
U2: () => { turnFace(up, 0); turnFace(up, 0); },
D: () => turnFace(down, 1),
"D'": () => { turnFace(down, 1); turnFace(down, 1); turnFace(down, 1); },
D2: () => { turnFace(down, 1); turnFace(down, 1); },
R: () => turnFace(right, 2),
"R'": () => { turnFace(right, 2); turnFace(right, 2); turnFace(right, 2); },
R2: () => { turnFace(right, 2); turnFace(right, 2); },
L: () => turnFace(left, 3),
"L'": () => { turnFace(left, 3); turnFace(left, 3); turnFace(left, 3); },
L2: () => { turnFace(left, 3); turnFace(left, 3); },
F: () => turnFace(front, 4),
"F'": () => { turnFace(front, 4); turnFace(front, 4); turnFace(front, 4); },
F2: () => { turnFace(front, 4); turnFace(front, 4); },
B: () => turnFace(back, 5),
"B'": () => { turnFace(back, 5); turnFace(back, 5); turnFace(back, 5); },
B2: () => { turnFace(back, 5); turnFace(back, 5); },
};
let up, down, right, left, front, back;
let easycam;
// eslint-disable-next-line no-unused-vars
function setup() {
createCanvas(WIDTH, HEIGHT, WEBGL);
setAttributes("antialias", true);
initPos();
easycam = createEasyCam();
easycam.rotateY(PI / 4);
easycam.rotateX(PI / 8);
easycam.setDistance(250);
easycam.setDistanceMin(200);
easycam.setDistanceMax(500);
createUI();
}
// eslint-disable-next-line no-unused-vars
function draw() {
background(0);
strokeWeight(3);
translate((-SIZE * 3) / 2, (-SIZE * 3) / 2, (SIZE * 3) / 2);
paintFace(up, 0, 0, -SIZE * 3, PI / 2, 0, 0);
paintFace(down, 0, SIZE * 3, 0, -PI / 2, 0, 0);
paintFace(right, SIZE * 3, 0, 0, 0, PI / 2, 0);
paintFace(left, 0, 0, -SIZE * 3, 0, -PI / 2, 0);
paintFace(front, 0, 0, 0, 0, 0, 0);
paintFace(back, SIZE * 3, 0, -SIZE * 3, 0, PI, 0);
}
function paintFace(face, x, y, z, rotX, rotY, rotZ) {
push();
translate(x, y, z);
rotateX(rotX);
rotateY(rotY);
rotateZ(rotZ);
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
fill(COLOR_MAP[face[i][j]]);
rect(i * SIZE, j * SIZE, SIZE, SIZE);
}
}
pop();
}
function scramble() {
let moves = [];
for (let i = 0; i < 20; i++) {
let move = MOVES[floor(random(0, MOVES.length))];
if (KEY_MAP[move]) KEY_MAP[move]();
moves.push(move);
}
print(moves);
document.getElementById("moves").innerHTML = moves.join(" ");
}
function initPos() {
up = new Array(3).fill(1).map(() => new Array(3).fill(1));
down = new Array(3).fill(2).map(() => new Array(3).fill(2));
right = new Array(3).fill(3).map(() => new Array(3).fill(3));
left = new Array(3).fill(4).map(() => new Array(3).fill(4));
front = new Array(3).fill(5).map(() => new Array(3).fill(5));
back = new Array(3).fill(6).map(() => new Array(3).fill(6));
}
function createUI() {
createButton("Scramble")
.mousePressed(scramble)
.position(width + 20, 20);
createButton("Reset")
.mousePressed(initPos)
.position(width + 20, 60);
let button_size = 30;
let spacing = 10;
createDiv("🔃")
.position(width + 20, 150)
.style("font-size", "20px");
createDiv("🔄️")
.position(width + 20 + button_size + spacing, 150)
.style("font-size", "20px");
createDiv("2️⃣")
.position(width + 20 + (button_size + spacing) * 2, 150)
.style("font-size", "20px");
MOVES.forEach((move, i) => {
let color = [0, 0, 0];
if (move.includes("U")) color = COLOR_MAP[1];
if (move.includes("D")) color = COLOR_MAP[2];
if (move.includes("R")) color = COLOR_MAP[3];
if (move.includes("L")) color = COLOR_MAP[4];
if (move.includes("F")) color = COLOR_MAP[5];
if (move.includes("B")) color = COLOR_MAP[6];
createButton(move)
.mousePressed(KEY_MAP[move])
.position(
10 + width + spacing + (i % 3) * (button_size + spacing),
180 + spacing + floor(i / 3) * (button_size + spacing),
)
.size(button_size, button_size)
.style("background-color", `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
});
selectAll("button").forEach((button) => {
button.style("font-size", "12pt");
button.style("font-weight", "bold");
button.style("text-align", "center");
});
// text for the scramble moves
createDiv("")
// position bottom with white letters
.position(20, height - 40)
.style("color", "white")
.style("font-size", "20px")
.id("moves");
}
function turnFace(face, n) {
let temp;
if (n == 0) {
temp = front[0][0];
front[0][0] = right[0][0];
right[0][0] = back[0][0];
back[0][0] = left[0][0];
left[0][0] = temp;
temp = front[1][0];
front[1][0] = right[1][0];
right[1][0] = back[1][0];
back[1][0] = left[1][0];
left[1][0] = temp;
temp = front[2][0];
front[2][0] = right[2][0];
right[2][0] = back[2][0];
back[2][0] = left[2][0];
left[2][0] = temp;
}
if (n == 1) {
temp = front[0][2];
front[0][2] = left[0][2];
left[0][2] = back[0][2];
back[0][2] = right[0][2];
right[0][2] = temp;
temp = front[1][2];
front[1][2] = left[1][2];
left[1][2] = back[1][2];
back[1][2] = right[1][2];
right[1][2] = temp;
temp = front[2][2];
front[2][2] = left[2][2];
left[2][2] = back[2][2];
back[2][2] = right[2][2];
right[2][2] = temp;
}
if (n == 2) {
temp = front[2][0];
front[2][0] = down[2][0];
down[2][0] = back[0][2];
back[0][2] = up[2][0];
up[2][0] = temp;
temp = front[2][1];
front[2][1] = down[2][1];
down[2][1] = back[0][1];
back[0][1] = up[2][1];
up[2][1] = temp;
temp = front[2][2];
front[2][2] = down[2][2];
down[2][2] = back[0][0];
back[0][0] = up[2][2];
up[2][2] = temp;
}
if (n == 3) {
temp = back[2][0];
back[2][0] = down[0][2];
down[0][2] = front[0][2];
front[0][2] = up[0][2];
up[0][2] = temp;
temp = back[2][1];
back[2][1] = down[0][1];
down[0][1] = front[0][1];
front[0][1] = up[0][1];
up[0][1] = temp;
temp = back[2][2];
back[2][2] = down[0][0];
down[0][0] = front[0][0];
front[0][0] = up[0][0];
up[0][0] = temp;
}
if (n == 4) {
temp = up[0][2];
up[0][2] = left[2][2];
left[2][2] = down[2][0];
down[2][0] = right[0][0];
right[0][0] = temp;
temp = up[1][2];
up[1][2] = left[2][1];
left[2][1] = down[1][0];
down[1][0] = right[0][1];
right[0][1] = temp;
temp = up[2][2];
up[2][2] = left[2][0];
left[2][0] = down[0][0];
down[0][0] = right[0][2];
right[0][2] = temp;
}
if (n == 5) {
temp = up[0][0];
up[0][0] = right[2][0];
right[2][0] = down[2][2];
down[2][2] = left[0][2];
left[0][2] = temp;
temp = up[1][0];
up[1][0] = right[2][1];
right[2][1] = down[1][2];
down[1][2] = left[0][1];
left[0][1] = temp;
temp = up[2][0];
up[2][0] = right[2][2];
right[2][2] = down[0][2];
down[0][2] = left[0][0];
left[0][0] = temp;
}
temp = face[0][0];
face[0][0] = face[0][2];
face[0][2] = face[2][2];
face[2][2] = face[2][0];
face[2][0] = temp;
temp = face[0][1];
face[0][1] = face[1][2];
face[1][2] = face[2][1];
face[2][1] = face[1][0];
face[1][0] = temp;
}
This code makes use of:
P5’s basic translation and rotation for the cube movements.
The easyCam library to move the camera around the cube.
A simple GUI with buttons to control the cube.