xxxxxxxxxx
// A classic tree search problem, solved(?) using the "ball of string" method.
// see: https://inventwithpython.com/pygame/chapter4.html
// Click mouse to scramble tiles, then again to unscramble.
// boolean, double subscripted array, array, class, gradient, random, mousePressed
'use strict()';
var sze = 6;
var tiles = [];
var grid = [];
var numMoves = 100;
var index = numMoves-1;
var moveData = [numMoves];
var lastMove = 4;
var currSpcePos;
var done = false;
var solve = false;
var started = false;
var scramble = false;
var isMoving = true;
//var h;
var move, click, applause;
function preload(){
click = loadSound("click.mp3");
applause = loadSound("applause.mp3");
}
function setup() {
createCanvas(620, 620);
colorMode(HSB, 360, 100, 100, 100);
rectMode(CENTER);
for (let i = 0; i < sze; i++) {
tiles[i] = new Array(sze);
grid[i] = new Array(sze);
}
//h = random(360);
initTilesAndGrid();
currSpcePos = new CRVals(sze-1, sze-1);
move = new CRVals();
click.setVolume(0.2);
}
function draw() {
background(0, 0, 0);
fill(10);
strokeWeight(2);
stroke(100);
rect(width/2, height/2, 610, 610);
if (!done && scramble) {
scrambleTiles();
}
if (done && scramble && solve) {
solvePuzzle();
}
//display tiles
for (let j = 0; j < sze; j++) {
for (let i = 0; i < sze; i++) {
if (tiles[i][j].ID != 0) {
tiles[i][j].display();
}
}
}
}
function mousePressed() {
if (!done) {
scramble = true;
} else if (done && scramble) {
solve = true;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
class Tile {
constructor(xin, yin, idin) {
this.x = xin;
this.y = yin;
this.ID = idin;
var num = random(360);
this.h = num;
}
display() {
let c1 = color(this.h, 100, 100);
let c2 = color(this.h, 100, 60);
let c3 = color(this.h, 100, 0);
let gradient = drawingContext.createLinearGradient(this.x, this.y -50, this.x, this.y +50);
gradient.addColorStop(0.0, c1);
gradient.addColorStop(0.85, c2);
gradient.addColorStop(1.0, c3);
drawingContext.fillStyle = gradient;
stroke(100);
strokeWeight(0.5);
rect(this.x, this.y, 97, 97, 20);
drawingContext.fillStyle = color;
fill(0, 0, 100, 35);
noStroke();
rect(this.x, this.y-41, 78, 8, 10);
textAlign(CENTER, CENTER);
fill(0);
textSize(40);
text(this.ID, this.x +3, this.y+4);
fill(60, 100, 90);
textSize(35);
text(this.ID, this.x, this.y);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
class CRVals { // col/row
constructor(xin, yin, d) {
this.x = xin;
this.y = yin;
this.direction = d;
}
setCRV(inst) {
this.x = inst.x;
this.y = inst.y;
this.direction = inst.direction;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// calc the tiles[][] "home" positions and
// store these positions in grid[][] for reference
function initTilesAndGrid() {
let s = 100;
let offset = 60;
let num = 1;
for (let r = 0; r < sze; r++) {
for (let c = 0; c < sze; c++) {
tiles[c][r] = new Tile(offset + c * s, offset + r * s, num);
grid[c][r] = createVector(offset + c * s, offset + r * s);
num++;
if (num > sze*sze-1) {
num = 0;
}
}
}
}
// calc random moves, store moves, update tiles.
function scrambleTiles() {
for (let i = 0; i < numMoves; i++) {
move.setCRV(calcRandMove(currSpcePos));
updateTiles(move);
// reverse direction before storing so that we can find our way home.
var dir = move.direction;
if (dir === 0) {
dir = 1;
} else if (dir === 1) {
dir = 0;
} else if (dir === 2) {
dir = 3;
} else if (dir === 3) {
dir = 2;
}
moveData[i] = new CRVals(currSpcePos.x, currSpcePos.y, dir);
currSpcePos.setCRV(move);
}
done = true;
}
function calcRandMove(current) {
while (true) {
let which = int(random(4)); // 0-1-2-3 up-down-right-left
if (which === 0 && current.y > 0 && lastMove != 1) {
// up
lastMove = 0;
return new CRVals(current.x, current.y - 1, 1);
} else if (which === 1 && current.y < sze-1 && lastMove != 0) {
//down
lastMove = 1;
return new CRVals(current.x, current.y + 1, 0);
} else if (which === 2 && current.x < sze-1 && lastMove != 3) {
// right
lastMove = 2;
return new CRVals(current.x + 1, current.y, 3);
} else if (which === 3 && current.x > 0 && lastMove != 2) {
// left
lastMove = 3;
return new CRVals(current.x - 1, current.y, 2);
}
}
}
function updateTiles(pos) {
// change xy locations
tiles[currSpcePos.x][currSpcePos.y].x = grid[pos.x][pos.y].x;
tiles[currSpcePos.x][currSpcePos.y].y = grid[pos.x][pos.y].y;
tiles[pos.x][pos.y].x = grid[currSpcePos.x][currSpcePos.y].x;
tiles[pos.x][pos.y].y = grid[currSpcePos.x][currSpcePos.y].y;
// change c/r locations
let hold = new Tile(tiles[currSpcePos.x][currSpcePos.y].x,
tiles[currSpcePos.x][currSpcePos.y].y,
tiles[currSpcePos.x][currSpcePos.y].ID);
hold.h = tiles[currSpcePos.x][currSpcePos.y].h;
tiles[currSpcePos.x][currSpcePos.y] = tiles[pos.x][pos.y];
tiles[pos.x][pos.y] = hold;
}
function solvePuzzle() {
let s = 4.0;
if (isMoving) {
let d = moveData[index].direction;
let x = moveData[index].x;
let y = moveData[index].y;
if (d === 0) {// up
tiles[x][y].y -= s;
if (tiles[x][y].y <= grid[x][y-1].y) {
let copy = new Tile(tiles[x][y].x, tiles[x][y].y, tiles[x][y].ID);
copy.h = tiles[x][y].h;
tiles[x][y] = tiles[x][y-1];
tiles[x][y-1] = copy;
isMoving = false;
click.play();
return 0;
}
} else if (d == 1) { // down
tiles[x][y].y += s;
if (tiles[x][y].y >= grid[x][y+1].y) {
let copy = new Tile(tiles[x][y].x, tiles[x][y].y, tiles[x][y].ID);
copy.h = tiles[x][y].h;
tiles[x][y] = tiles[x][y+1];
tiles[x][y+1] = copy;
isMoving = false;
click.play();
return 0;
}
} else if (d == 2) { //right
tiles[x][y].x += s;
if (tiles[x][y].x >= grid[x+1][y].x) {
let copy = new Tile(tiles[x][y].x, tiles[x][y].y, tiles[x][y].ID);
copy.h = tiles[x][y].h;
tiles[x][y] = tiles[x+1][y];
tiles[x+1][y] = copy;
isMoving = false;
click.play();
return 0;
}
} else if (d == 3) { //left
tiles[x][y].x -= s;
if (tiles[x][y].x <= grid[x-1][y].x) {
let copy = new Tile(tiles[x][y].x, tiles[x][y].y, tiles[x][y].ID);
copy.h = tiles[x][y].h;
tiles[x][y] = tiles[x-1][y];
tiles[x-1][y] = copy;
isMoving = false;
click.play();
return 0;
}
}
}
if (!isMoving) {
index--;
if (index < 0) {
isMoving = false;
if(!started){
applause.play();
started = true;
}
return 0;
}
isMoving = true;
}
return 0;
}