I’m currently building a pattern for an Art Car at Burning Man, and I’m running into a funny error with it.
The basics of the pattern is to be a Rubics cube. It mixes itself up, and then solves it by reversing the mixes. The pattern itself works fine, but if I have it do too many moves, I end up with an Array index out of bounds error in either the rotateRow or RotateColumn functions, on the temp rows array declaration
var tempRows = array(300);
This doesn’t make any sense as I’m just creating that array.
I recently converted the program from using 81 pixels a face to 225, and I’ve seen a decrease in the number of allowable moves. Currently it will succeed at 8 moves (so 16 round trip) sometimes work at 9 and 10, and always fail at 11 (22 moves round trip)
I’ve added debugging to it, but it’s always telling me it’s successfully undone all the moves, which is not true.
Code
var RED = 0, GREEN = 0.333, BLUE = 0.667, YELLOW = 0.167, ORANGE = 0.083, WHITE = 5;
var maxMoves = 10; // Adjust as needed
var moves = array(maxMoves * 3);
var moveIndex = 0;
var isMixing = true;
export var moveCount = 0;
var framesPerMove = 2;
export var debugLastOperation = 0;
export var debugMoveIndex = 0;
export var debugIsMixing = 1;
export var debugLastMoveType = 0;
export var debugLastGroupIndex = 0;
export var debugLastMoveDirection = 0;
export var debugSolveComplete = 0;
export var debugTotalFrames = 0;
var solveComplete = false;
function createFilledArray(size, value) {
var arr = array(size);
for (var i = 0; i < size; i++) {
arr[i] = value;
}
return arr;
}
// Cube representation (6 sides, each side is 15x15)
var cube = [
createFilledArray(225, WHITE), // Front (0)
createFilledArray(225, GREEN), // Left (1)
createFilledArray(225, YELLOW), // Back (2)
createFilledArray(225, BLUE), // Right (3)
createFilledArray(225, ORANGE), // Top (4)
createFilledArray(225, RED) // Bottom (5)
];
function rotateface(faceIndex, clockwise) {
var oldFace = cube[faceIndex];
var newFace = array(225);
debugLastOperation = 1; // rotateface
for (var i = 0; i < 15; i++) {
for (var j = 0; j < 15; j++) {
if (clockwise) {
newFace[j * 15 + (14 - i)] = oldFace[i * 15 + j];
} else {
newFace[(14 - j) * 15 + i] = oldFace[i * 15 + j];
}
}
}
cube[faceIndex] = newFace;
}
function rotateRow(rowGroup, direction) {
var startRow = rowGroup * 5;
var tempRows = array(300);
debugLastOperation = 2; // rotateRow
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
tempRows[i * 15 + j] = cube[0][(startRow + i) * 15 + j]; // Front
tempRows[(i + 5) * 15 + j] = cube[3][(startRow + i) * 15 + j]; // Right
tempRows[(i + 10) * 15 + j] = cube[2][(startRow + i) * 15 + j]; // Back
tempRows[(i + 15) * 15 + j] = cube[1][(startRow + i) * 15 + j]; // Left
}
}
if (direction > 0) { // Rotate right
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
cube[3][(startRow + i) * 15 + j] = tempRows[i * 15 + j]; // Front to Right
cube[2][(startRow + i) * 15 + j] = tempRows[(i + 5) * 15 + j]; // Right to Back
cube[1][(startRow + i) * 15 + j] = tempRows[(i + 10) * 15 + j]; // Back to Left
cube[0][(startRow + i) * 15 + j] = tempRows[(i + 15) * 15 + j]; // Left to Front
}
}
if (rowGroup === 0) rotateface(4, true); // Top face clockwise
if (rowGroup === 2) rotateface(5, false); // Bottom face counterclockwise
} else { // Rotate left
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
cube[1][(startRow + i) * 15 + j] = tempRows[i * 15 + j]; // Front to Left
cube[0][(startRow + i) * 15 + j] = tempRows[(i + 5) * 15 + j]; // Right to Front
cube[3][(startRow + i) * 15 + j] = tempRows[(i + 10) * 15 + j]; // Back to Right
cube[2][(startRow + i) * 15 + j] = tempRows[(i + 15) * 15 + j]; // Left to Back
}
}
if (rowGroup === 0) rotateface(4, false); // Top face counterclockwise
if (rowGroup === 2) rotateface(5, true); // Bottom face clockwise
}
}
function rotateColumn(colGroup, direction) {
var startCol = colGroup * 5;
var tempCols = array(300);
debugLastOperation = 3; // rotateColumn
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
tempCols[i * 15 + j] = cube[4][j * 15 + (startCol + i)]; // Top
tempCols[(i + 5) * 15 + j] = cube[0][j * 15 + (startCol + i)]; // Front
tempCols[(i + 10) * 15 + j] = cube[5][j * 15 + (startCol + i)]; // Bottom
tempCols[(i + 15) * 15 + j] = cube[2][(14 - j) * 15 + (14 - startCol - i)]; // Back (reversed)
}
}
if (direction > 0) { // Rotate down
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
cube[0][j * 15 + (startCol + i)] = tempCols[i * 15 + j]; // Top to Front
cube[5][j * 15 + (startCol + i)] = tempCols[(i + 5) * 15 + j]; // Front to Bottom
cube[2][(14 - j) * 15 + (14 - startCol - i)] = tempCols[(i + 10) * 15 + j]; // Bottom to Back (reversed)
cube[4][j * 15 + (startCol + i)] = tempCols[(i + 15) * 15 + j]; // Back to Top
}
}
if (colGroup === 0) rotateface(1, false); // Left face clockwise
if (colGroup === 2) rotateface(3, true); // Right face clockwise
} else { // Rotate up
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 15; j++) {
cube[2][(14 - j) * 15 + (14 - startCol - i)] = tempCols[i * 15 + j]; // Top to Back (reversed)
cube[5][j * 15 + (startCol + i)] = tempCols[(i + 15) * 15 + j]; // Back to Bottom
cube[0][j * 15 + (startCol + i)] = tempCols[(i + 10) * 15 + j]; // Bottom to Front
cube[4][j * 15 + (startCol + i)] = tempCols[(i + 5) * 15 + j]; // Front to Top
}
}
if (colGroup === 0) rotateface(1, true); // Left face counterclockwise
if (colGroup === 2) rotateface(3, false); // Right face counterclockwise
}
}
function performNextMove() {
debugLastOperation = 4; // performNextMove start
debugMoveIndex = moveIndex;
debugIsMixing = isMixing ? 1 : 0;
if (isMixing && moveCount < maxMoves) {
var moveType = random(2);
var groupIndex = floor(random(3));
var moveDirection = random(1) > 0.5 ? 1 : -1;
debugLastMoveType = moveType;
debugLastGroupIndex = groupIndex;
debugLastMoveDirection = moveDirection;
if (moveType > 1.0) {
rotateRow(groupIndex, moveDirection);
} else {
rotateColumn(groupIndex, moveDirection);
}
recordMove(moveType, groupIndex, moveDirection);
moveCount++;
if (moveCount == maxMoves) {
isMixing = false;
moveIndex = maxMoves * 3; // Ensure moveIndex is at the end
}
debugLastOperation = 5; // performNextMove: mixed
return true;
} else if (!isMixing && moveCount > 0) {
moveCount--;
moveIndex -= 3;
var lastMoveType = moves[moveIndex];
var lastGroupIndex = moves[moveIndex + 1];
var lastMoveDirection = moves[moveIndex + 2]; // Note: We're not negating this anymore
debugLastMoveType = lastMoveType;
debugLastGroupIndex = lastGroupIndex;
debugLastMoveDirection = lastMoveDirection;
if (lastMoveType > 1.0) {
rotateRow(lastGroupIndex, -lastMoveDirection); // Negate direction here
} else {
rotateColumn(lastGroupIndex, -lastMoveDirection); // Negate direction here
}
debugLastOperation = 7; // performNextMove: solved
return true;
}
debugLastOperation = 12; // All moves undone
return false;
}
function recordMove(moveType, groupIndex, moveDirection) {
debugLastOperation = 9; // recordMove start
if (moveIndex < maxMoves * 3 - 2) {
moves[moveIndex] = moveType;
moves[moveIndex + 1] = groupIndex;
moves[moveIndex + 2] = moveDirection;
moveIndex += 3;
debugLastOperation = 10; // recordMove: recorded
} else {
isMixing = false;
debugLastOperation = 11; // recordMove: max moves reached
}
}
var frameCounter = 0;
export function beforeRender(delta) {
debugTotalFrames++;
if (debugSolveComplete == 1) {
return;
}
frameCounter++;
if (frameCounter >= framesPerMove) {
if (!performNextMove()) {
debugSolveComplete = 1;
}
frameCounter = 0;
}
}
export function render(index) {
var faceIndex, x, y;
if (index < 225) {
faceIndex = 0; // Front
} else if (index < 450) {
faceIndex = 1; // Left
index -= 225;
} else if (index < 675) {
faceIndex = 2; // Back
index -= 450;
} else if (index < 900) {
faceIndex = 3; // Right
index -= 675;
} else if (index < 1125) {
faceIndex = 4; // Top
index -= 900;
} else if (index < 1350) {
faceIndex = 5; // Bottom
index -= 1125;
} else {
rgb(0, 0, 0); // Set to black if outside the cube
return;
}
x = index % 15;
y = floor(index / 15);
var pixelIndex = y * 15 + x;
var color = cube[faceIndex][pixelIndex];
if (color < 1) {
hsv(color, 1, 1);
} else if (color >= 1) {
hsv(0, 0, 1); // White
}
}
Mapping
function (pixelCount) {
width = 15
//generate a 2d matrix in 3d space
//targets is an array of source coordinates 0 = fixed, 1 = row, 2 = column
//e.g. [1,2,0] will generate rows = x, cols = y, and z is fixed (at zero)
//sign indicates direction so -2 is columns in reverse order
//offsets lets you translate the coordinates by some offset
function side(targets, offsets) {
var matrix = [], coords, row, col
for (i = 0; i < width * width; i++) {
row = Math.floor(i / width)
col = i % width
// col = row % 2 == 1 ? width - 1 - col : col //zigzag
coords = [0, row, col]
matrix.push(targets.map(function (target, index) {
var coord = coords[Math.abs(target)]
if (target < 0)
coord = width - 1 - coord
return coord + offsets[index]
}))
}
return matrix
}
var map = []
var gap = .75
map = map.concat(side([2, 1, 0], [0, 0, width - 1 + gap]))
map = map.concat(side([0, 1, 2], [-gap, 0, 0]))
map = map.concat(side([1, 0, 2], [0, width - 1 + gap, 0]))
map = map.concat(side([0, -1, 2], [width - 1 + gap, 0, 0]))
map = map.concat(side([-1, 0, 2], [0, -gap, 0]))
map = map.concat(side([-1,2,0], [0,0,-.75]))
return map
}