import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.applet.*;
public class puzzle extends Applet implements ActionListener, Runnable {
Thread engine = null;
Image baseImage;
piece pieces[][];
MediaTracker tracker;
int cols = 4, rows = 4; // number of columns and rows
int totalh, totalw; // dimensions of image
int h, w; // dimensions of each piece
int scramble; // number of scramble moves
int moveCounter = 0; // number of moves so far
Button rstrt = new Button("Try again"); // the restart button
boolean donep = false;
boolean full_repaint = true;
Image offImage = null;
Graphics offGraphics = null;
public void init() {
setLayout(new BorderLayout());
rstrt.addActionListener(this);
tracker = new MediaTracker(this);
Dimension d = this.size();
offImage = createImage(d.width,d.height);
offGraphics = offImage.getGraphics();
// get params with defaults
String s = getParameter("img");
if (s == null)
s = "waterbill.jpg";
baseImage = getImage(getCodeBase(), s);
tracker.addImage(baseImage,0);
s = getParameter("rows");
if (s != null)
rows = Integer.valueOf(s).intValue();
s = getParameter("cols");
if (s != null)
cols = Integer.valueOf(s).intValue();
s = getParameter("scramble");
if (s != null)
scramble = Integer.valueOf(s).intValue();
else
scramble = 2*cols*rows;
this.init_board();
moveCounter = 0;
Panel pnl = new Panel();
pnl.add(rstrt);
add("South", pnl);
}
public void init_board() {
// wait for baseImage to fully load
try {
tracker.waitForID(0); }
catch (InterruptedException e) {
return; }
totalh = baseImage.getHeight(this);
totalw = baseImage.getWidth(this);
pieces = new piece[cols][rows];
// return if baseImage not available yet
totalh = totalh - (totalh % rows);
totalw = totalw - (totalw % cols);
h = totalh / rows;
w = totalw / cols;
// construct pieces
ImageFilter cropfilter;
ImageProducer prod;
Image new_img;
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++) {
cropfilter = new CropImageFilter(c*w, r*h, w, h);
prod = new FilteredImageSource(baseImage.getSource(),cropfilter);
new_img = createImage(prod);
tracker.addImage(new_img,1);
pieces[c][r] = new piece(new_img,c,r);
}
// make empty piece
int b_col = cols-1;
int b_row = 0;
pieces[b_col][b_row] = null;
this.scrambler();
// make sure pieces are loaded
try {
tracker.waitForID(1); }
catch (InterruptedException e) {
return; }
donep = false;
full_repaint = true;
}
public void scrambler() {
int b_row = 0;
int b_col = cols-1;
int poss[][] = new int[cols][rows];
int c;
int b_row2 = b_row, b_col2 = b_col; // avoid repeats
for(int i = 0; i < scramble; i++) {
c = 0;
if ((b_col+1 < cols) && !((b_col+1 == b_col2) && (b_row == b_row2))) {
poss[c][0] = b_col+1;
poss[c][1] = b_row;
c++; }
if ((b_col-1 >= 0) && !((b_col-1 == b_col2) && (b_row == b_row2))) {
poss[c][0] = b_col-1;
poss[c][1] = b_row;
c++; }
if ((b_row+1 < rows) && !((b_col == b_col2) && (b_row+1 == b_row2))) {
poss[c][0] = b_col;
poss[c][1] = b_row+1;
c++; }
if ((b_row-1 >= 0) && !((b_col == b_col2) && (b_row-1 == b_row2))) {
poss[c][0] = b_col;
poss[c][1] = b_row-1;
c++; }
c = (int)Math.round((c-1)*Math.random());
pieces[b_col][b_row] = pieces[poss[c][0]][poss[c][1]];
b_col2 = b_col;
b_row2 = b_row;
b_col = poss[c][0];
b_row = poss[c][1];
pieces[b_col][b_row] = null;
}
donep = false;
full_repaint = true;
}
public void paint(Graphics g) {
full_repaint = false;
if (baseImage == null)
return;
if (donep) {
g.drawImage(baseImage,0,0,this);
return;
}
// draw pieces
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
if (pieces[c][r] == null) {
offGraphics.setColor(Color.black);
offGraphics.fillRect(c*w, r*h, w, h); }
else
offGraphics.drawImage(pieces[c][r].pict,c*w,r*h,this);
// draw grid
for(int i = 0; i <= cols; i++)
offGraphics.drawLine(i*w,0,i*w,totalh);
for(int i = 0; i <= rows; i++)
offGraphics.drawLine(0,i*h,totalw,i*h);
g.drawImage(offImage,0,0,this);
String moveDisplay = "Moves: " + moveCounter;
g.drawString(moveDisplay, (totalw / 2) - 20, totalh + 15);
}
public void update(Graphics g) {
paint(g);
}
public boolean mouseDown(java.awt.Event evt, int x, int y) {
int c = x / w;
int r = y / h;
if ((c > cols-1) || (r > rows-1))
return true;
if (donep) {
moveCounter = 0;
this.init_board();
full_repaint = true;
return true;
}
// check for move
boolean movep = false;
if (pieces[c][r] == null) return true;
if ((c < cols-1) && (pieces[c+1][r] == null)) {
pieces[c+1][r] = pieces[c][r];
movep = true; }
if ((c > 0) && (pieces[c-1][r] == null)) {
pieces[c-1][r] = pieces[c][r];
movep = true; }
if ((r < rows-1) && (pieces[c][r+1] == null)) {
pieces[c][r+1] = pieces[c][r];
movep = true; }
if ((r > 0) && (pieces[c][r-1] == null)) {
pieces[c][r-1] = pieces[c][r];
movep = true; }
if (movep) {
pieces[c][r] = null;
full_repaint = true;
moveCounter++;
// check for done
donep = true;
piece p;
for (r = 0; r < rows; r++)
for (c = 0; c < cols; c++) {
p = pieces[c][r];
if ((p != null) && ((p.col != c) || (p.row != r)))
donep = false;
}
}
return true;
}
public void start() {
if (engine == null) {
engine = new Thread(this);
engine.start(); }
}
public void stop() {
if (engine != null && engine.isAlive()) {
engine.stop(); }
engine = null;
}
public void run() {
Thread me = Thread.currentThread();
while (engine == me) {
try {Thread.currentThread().sleep(100);} catch (InterruptedException e){}
if (full_repaint) {
repaint(); }
}
}
public void actionPerformed(ActionEvent e) {
if("Try again".equals(e.getActionCommand())) {
donep = false;
moveCounter = 0;
this.init_board();
full_repaint = true;
}
}
}
class piece {
public Image pict;
public int col,row;
piece(Image p, int c, int r) {
pict = p;
col = c;
row = r;
}
}