Another way with Box2D. just tiles moving, nothing to do with the puzzle itself. and it may not apply if you don’t actually want the tiles sliding.
import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.dynamics.*;
Box2DProcessing box2d;
ArrayList boundaries;
Box[][] tiles;
Box box;
Spring spring;
Surface surface;
void setup() {
size(321, 321);
box2d = new Box2DProcessing(this);
box2d.createWorld();
box2d.setGravity(0, 0);
addBoundaries();
surface = new Surface(width/2, height/2);
tiles = new Box[3][3];
initTiles();
bindTilesToSurface();
spring = new Spring();
}
void draw() {
background(255);
box2d.step();
surface.display();
spring.update(mouseX, mouseY);
displayWalls();
displayTiles();
//spring.display();
}
void mousePressed() {
int n = 0;
for (int c = 0; c < tiles.length; c++) {
for (int r = 0; r < tiles[0].length && n < 8; r++) {
if (tiles[c][r].contains(mouseX, mouseY)) {
spring.bind(mouseX, mouseY, tiles[c][r]);
}
n++;
}
}
}
void mouseReleased() {
spring.destroy();
}
void displayWalls() {
for (int i = 0; i < boundaries.size(); i++) {
Boundary wall = (Boundary) boundaries.get(i);
wall.display();
}
}
void displayTiles() {
int n = 0;
for (int c = 0; c < tiles.length; c++) {
for (int r = 0; r < tiles[0].length && n < 8; r++) {
tiles[c][r].display();
n++;
}
}
}
void addBoundaries() {
boundaries = new ArrayList();
boundaries.add(new Boundary(width/2, height-5, width, 10, 0));
boundaries.add(new Boundary(width/2, 5, width, 10, 0));
boundaries.add(new Boundary(width-5, height/2, 10, height, 0));
boundaries.add(new Boundary(5, height/2, 10, height, 0));
}
void initTiles() {
int n = 0;
for (int c = 0; c < tiles.length; c++) {
for (int r = 0; r < tiles[0].length && n < 8; r++) {
tiles[c][r] = new Box(50 + 100 * c, 50 + 100 * r);
n++;
}
}
}
void bindTilesToSurface() {
int n = 0;
for (int c = 0; c < tiles.length; c++) {
for (int r = 0; r < tiles[0].length && n < 8; r++) {
tiles[c][r].bind(surface);
n++;
}
}
}
class Boundary {
float x, y, w, h;
Body b;
Boundary(float x, float y, float w, float h, float a) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.angle = a;
bd.position.set(box2d.coordPixelsToWorld(x, y));
b = box2d.createBody(bd);
b.createFixture(ps, 1);
}
void display() {
fill(75);
noStroke();
rectMode(CENTER);
float a = b.getAngle();
pushMatrix();
translate(x, y);
rotate(-a);
rect(0, 0, w, h);
popMatrix();
}
}
class Box {
FrictionJoint frictionJoint;
Body body;
float x, y;
float w, h;
Box(float x, float y) {
this.x = x;
this.y = y;
w = 100;
h = 100;
makeBody(new Vec2(x, y));
}
boolean contains(float x, float y) {
Vec2 worldPoint = box2d.coordPixelsToWorld(x, y);
Fixture f = body.getFixtureList();
boolean inside = f.testPoint(worldPoint);
return inside;
}
void makeBody(Vec2 center) {
BodyDef bd = new BodyDef();
bd.type = BodyType.DYNAMIC;
bd.position.set(box2d.coordPixelsToWorld(center));
bd.fixedRotation = true;
body = box2d.createBody(bd);
PolygonShape ps = new PolygonShape();
Vec2[] vertices = new Vec2[8];
vertices[0] = box2d.vectorPixelsToWorld(new Vec2(-50, -48));
vertices[1] = box2d.vectorPixelsToWorld(new Vec2(-50, 48));
vertices[2] = box2d.vectorPixelsToWorld(new Vec2(-48, 50));
vertices[3] = box2d.vectorPixelsToWorld(new Vec2(48, 50));
vertices[4] = box2d.vectorPixelsToWorld(new Vec2(50, 48));
vertices[5] = box2d.vectorPixelsToWorld(new Vec2(50, -45));
vertices[6] = box2d.vectorPixelsToWorld(new Vec2(48, -50));
vertices[7] = box2d.vectorPixelsToWorld(new Vec2(-48, -50));
ps.set(vertices, vertices.length);
FixtureDef fd = new FixtureDef();
fd.shape = ps;
fd.density = 1;
fd.friction = 0;
fd.restitution = 0.0;
body.createFixture(fd);
}
void display() {
Vec2 pos = box2d.getBodyPixelCoord(body);
float a = body.getAngle();
pushMatrix();
translate(pos.x, pos.y);
rotate(-a);
fill(255, 255, 240);
stroke(125, 125, 110);
strokeWeight(2);
rectMode(CENTER);
rect(0, 0, w, h, 5);
popMatrix();
}
void killBody() {
box2d.destroyBody(body);
}
void bind(Surface surface) {
FrictionJointDef fj = new FrictionJointDef();
fj.bodyA = body;
fj.bodyB = surface.body;
fj.maxForce = 1000;
fj.maxTorque = 0;
fj.collideConnected = false;
frictionJoint = (FrictionJoint) box2d.world.createJoint(fj);
}
}
class Spring {
MouseJoint mouseJoint;
Spring() {
mouseJoint = null;
}
void update(float x, float y) {
if (mouseJoint != null) {
Vec2 mouseWorld = box2d.coordPixelsToWorld(x, y);
mouseJoint.setTarget(mouseWorld);
}
}
void display() {
if (mouseJoint != null) {
Vec2 v1 = new Vec2(0, 0);
mouseJoint.getAnchorA(v1);
Vec2 v2 = new Vec2(0, 0);
mouseJoint.getAnchorB(v2);
v1 = box2d.coordWorldToPixels(v1);
v2 = box2d.coordWorldToPixels(v2);
stroke(0);
strokeWeight(1);
line(v1.x, v1.y, v2.x, v2.y);
}
}
void bind(float x, float y, Box box) {
MouseJointDef md = new MouseJointDef();
md.bodyA = box2d.getGroundBody();
md.bodyB = box.body;
Vec2 mp = box2d.coordPixelsToWorld(x, y);
md.target.set(mp);
md.maxForce = 1000.0 * box.body.m_mass;
md.frequencyHz = 5.0;
md.dampingRatio = 0.9;
mouseJoint = (MouseJoint) box2d.world.createJoint(md);
}
void destroy() {
if (mouseJoint != null) {
box2d.world.destroyJoint(mouseJoint);
mouseJoint = null;
}
}
}
class Surface {
FrictionJoint frictionJoint;
Body body;
float x, y, w, h;
Surface(float x, float y) {
this.x = x;
this.y = y;
w = width;
h = height;
makeBody(new Vec2(x, y), w, h);
}
void display() {
Vec2 pos = box2d.getBodyPixelCoord(body);
rectMode(CENTER);
pushMatrix();
translate(pos.x, pos.y);
fill(0, 127, 0);
noStroke();
rect(0, 0, w, h);
popMatrix();
}
void makeBody(Vec2 center, float w, float h) {
BodyDef bd = new BodyDef();
bd.type = BodyType.STATIC;
bd.position.set(box2d.coordPixelsToWorld(center));
bd.fixedRotation = true;
body = box2d.createBody(bd);
PolygonShape ps = new PolygonShape();
float box2dW = box2d.scalarPixelsToWorld(w/2);
float box2dH = box2d.scalarPixelsToWorld(h/2);
ps.setAsBox(box2dW, box2dH);
FixtureDef fd = new FixtureDef();
fd.shape = ps;
body.createFixture(fd);
}
}