xxxxxxxxxx
226
// Definitely needs to be refactored later!
// This is fragile because I've used a hardcoded timeout to wait for moves animations to be finished. Awaiting a promise or something else would be a better solution.
// Solve function doesn't really 'solve' the puzzle, it just plays the moves made in reverse order.
// Clicking buttons again before the shuffle or solve process is finished will break things.
let ww = 400;
let hh = ww;
let cnvW = 400;
let cnvH = 400;
let source;
let resizedSource;
let tiles = [];
let cols = 5; // don't set this too high or it will crash the browser
let rows = cols;
let tileSize;
let blank;
let movesMade = [];
let btn1, btn2;
let delay = 100;
let firstMoveMade = false;
let lastPickedIndex;
let suffleCount = cols * cols * 2;
function preload() {
source = loadImage("572px-A_pinwheel.jpg");
// image is public domain
// https://commons.wikimedia.org/wiki/File:A_pinwheel.jpg
}
const resizeAndCrop = () => {
let minSide = min(source.width, source.height);
let maxSide = max(source.width, source.height);
let scaleFactor = ww * (maxSide / minSide);
source.resize(scaleFactor, 0);
resizedSource = createImage(ww, hh);
let hw = hh / 2;
let x1 = floor(source.width / 2) - hw;
let x2 = x1 + ww;
let y1 = floor(source.height / 2) - hw;
let y2 = y1 + hh;
resizedSource.copy(source, x1, y1, x2, y2, 0, 0, x2, y2);
};
function setup() {
let cnv = createCanvas(cnvW, cnvH);
tileSize = ww / cols;
resizeAndCrop();
for (let j = 0; j < rows; j++) {
for (let i = 0; i < cols; i++) {
let x = i * tileSize;
let y = j * tileSize;
let img = createImage(tileSize, tileSize);
img.copy(
resizedSource,
x,
y,
tileSize,
tileSize,
0,
0,
tileSize,
tileSize
);
let index = i + j * cols;
let tile = new Tile(index, img, x, y, tileSize, false, 255);
tiles.push(tile);
}
}
blank = tiles[tiles.length - 1];
blank.imgOpacity = 0;
blank.isBlank = true;
// cnv.position(windowWidth / 2 - cnvW / 2, windowHeight / 2 - cnvH / 2);
createP("");
btn1 = createButton(`Shuffle (${suffleCount} random moves)`);
btn2 = createButton("Solve");
btn1.mousePressed(() => {
shuffleArray(suffleCount);
});
btn2.mousePressed(() => {
solveArray(movesMade);
});
}
function mouseClicked() {
firstMoveMade = true;
if (!tiles.some((t) => t.moving)) {
for (let i = 0; i < tiles.length; i++) {
tiles[i].clicked(mouseX, mouseY);
}
}
}
const setBlankXY = (x, y) => {
blank.x = x;
blank.y = y;
};
const checkCurrentBoard = () => {
let currBoard = [];
for (let j = 0; j < rows; j++) {
for (let i = 0; i < cols; i++) {
let x = i * tileSize + tileSize / 2;
let y = j * tileSize + tileSize / 2;
let currentTile = tiles.find((tile) =>
testIntersection(
x,
y,
tile.x + tile.size / 2,
tile.y + tile.size / 2,
tile.size / 2
)
);
currBoard.push(currentTile.index);
}
}
return currBoard;
};
const testIntersection = (x, y, x2, y2) => {
let d = dist(x, y, x2, y2);
return d < tileSize / 2;
};
const shuffleArray = (count) => {
firstMoveMade = true;
for (let i = 0; i < count; i++) {
setTimeout(() => {
let movables = findMovableTiles();
movables = movables.filter((t) => t.index != lastPickedIndex);
let picked = random(movables);
lastPickedIndex = picked.index;
moveTile(picked);
}, delay * i);
}
};
const moveTile = (picked) => {
picked.setTarget(blank.x, blank.y);
};
const solveArray = (arr) => {
let tmp = Array.from(arr);
tmp.reverse();
for (let i = 0; i < tmp.length; i++) {
setTimeout(() => {
moveTile(tmp[i]);
}, delay * i);
}
};
const findMovableTiles = () => {
let movables = [];
let x = blank.x + tileSize / 2;
let y = blank.y + tileSize / 2;
let leftT = tiles.find((tile) =>
testIntersection(
x - tileSize,
y,
tile.x + tile.size / 2,
tile.y + tile.size / 2,
tile.size / 2
)
);
let rightT = tiles.find((tile) =>
testIntersection(
x + tileSize,
y,
tile.x + tile.size / 2,
tile.y + tile.size / 2,
tile.size / 2
)
);
let topT = tiles.find((tile) =>
testIntersection(
x,
y - tileSize,
tile.x + tile.size / 2,
tile.y + tile.size / 2,
tile.size / 2
)
);
let bottomT = tiles.find((tile) =>
testIntersection(
x,
y + tileSize,
tile.x + tile.size / 2,
tile.y + tile.size / 2,
tile.size / 2
)
);
if (leftT) movables.push(leftT);
if (rightT) movables.push(rightT);
if (topT) movables.push(topT);
if (bottomT) movables.push(bottomT);
return movables;
};
function draw() {
background(255, 255, 255, 100);
for (let i = tiles.length - 1; i >= 0; i--) {
tiles[i].move();
tiles[i].show();
}
if (isSolved() && firstMoveMade) {
console.log("SOLVED");
movesMade = [];
firstMoveMade = false;
// noLoop();
}
}
const isSolved = () => {
if (!tiles.some((t) => t.moving)) {
return tiles.map((t) => t.index).join() == checkCurrentBoard().join();
}
};