/*
Latticeland needs your help!

Once upon a time, Latticeland was a peaceful place. It was inhabited only by spins.
The spins lived happily side by side and abided by the Laws of the T-J Model: adjacent spins should point in opposite directions, and no two spins should live on the same site.
But one day, a naughty demon appeared. It zapped one of the spins, leaving behind an empty space called a hole, which it then pushed all over the place, leaving behind a string of frustrated spins pointing in the same direction as their neighbours.
Can you undo the mess and restore peace and happiness to Latticeland?
Tips
- Use the arrow keys to move the hole around.
- Start from one corner and proceed towards the other corner.
- Count the numbers of up and down spins in each region; if there is a local excess of up spins, you will need to move the hole round and round in a loop to bring in some down spins.
- If the demon made 100 moves, you can definitely solve the puzzle in 100 moves or less; the solution is not unique.
Credits
- Programming, graphics, and music: Yen Lee Loh
- Sound effects: Mark Rae, Arthur van Hoff, Kathy Walrath
- t-J model: P. W. Anderson, Science 235, 1196 (1987); surely a lot of other people contributed to our understanding ...?
*/
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.net.URL;
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
public class tj extends Applet
implements Runnable, ComponentListener, KeyListener {
final int STAGE_INIT=0,STAGE_SCRAMBLING=1,STAGE_PLAYING=2,STAGE_WON=3;
Thread thread1=null;
Image image1;
String message1;
int size0x,size0y;
int size1x,size1y,dx1,dy1,xoff,yoff;
int imx,imy;
int xmax,ymax,demonmoves;
int playermove;
int s[][]; //don't use char because char arithmetic doesn't seem to work
int xx[][],yy[][];
int hx,hy; // hole coordinate
AudioClip acMove,acVictory,acBackground; // should init to null. I will risk this
Image imHole,imsHole;
Image imHappyUp,imsHappyUp;
Image imHappyDown,imsHappyDown;
Image imUnhappyUp,imsUnhappyUp;
Image imUnhappyDown,imsUnhappyDown;
int delay=100;
int gameStage;
//========== My own methods ==========
public void myInit() {
int i,j,x,y,x2,y2,x12,y12;
for (x=0; x 0) {
happy=false; g.drawLine(x*dx1+xb+xoff, y*dy1+yoff, (x+1)*dx1-xb+xoff, y*dy1+yoff);
}
if (x>0 && s[x][y]*s[x-1][y] > 0) {
happy=false; g.drawLine(x*dx1-xb+xoff, y*dy1+yoff, (x-1)*dx1+xb+xoff, y*dy1+yoff);
}
if (y 0) {
happy=false; g.drawLine(x*dx1+xoff, y*dy1+yb+yoff, x*dx1+xoff, (y+1)*dy1-yb+yoff);
}
if (y>0 && s[x][y]*s[x][y-1] > 0) {
happy=false; g.drawLine(x*dx1+xoff, y*dy1-yb+yoff, x*dx1+xoff, (y-1)*dy1+yb+yoff);
}
allhappy=allhappy && happy;
if (s[x][y]==(int)1) {
if (happy) g.drawImage(imsHappyUp, xx[x][y], yy[x][y], null);
else g.drawImage(imsUnhappyUp, xx[x][y], yy[x][y], null);
} else if (s[x][y] == (int)-1) {
if (happy) g.drawImage(imsHappyDown, xx[x][y], yy[x][y], null);
else g.drawImage(imsUnhappyDown, xx[x][y], yy[x][y], null);
} else {
g.drawImage(imsHole, xx[x][y], yy[x][y], null);
}
}
if (allhappy && gameStage==STAGE_PLAYING) { //won!
acBackground.stop();
acVictory.play();
message1="Congratulations, you did it!";
gameStage=STAGE_WON;
}
}
public int sgn(int i) {return (i>0?1:0)-(i<0?1:0);}
//========== Extend Applet: init(), run(), paint(), update() ==========
public void init() {
try {
acMove = getAudioClip(getCodeBase(), "bubble1.wav");
acVictory = getAudioClip(getCodeBase(), "victory.wav");
acBackground = getAudioClip(getCodeBase(), "song.mid");
imHole = getImage(getCodeBase(), "hole.png");
imHappyUp = getImage(getCodeBase(), "happyup.png");
imHappyDown = getImage(getCodeBase(), "happydown.png");
imUnhappyUp = getImage(getCodeBase(), "unhappyup.png");
imUnhappyDown = getImage(getCodeBase(), "unhappydown.png");
} catch (Exception e2) {}
try {xmax=Integer.parseInt(getParameter("XMAX"));}
catch (Exception e) {xmax=10;}
try {ymax=Integer.parseInt(getParameter("YMAX"));}
catch (Exception e) {ymax=10;}
try {demonmoves=Integer.parseInt(getParameter("DEMONMOVES"));}
catch (Exception e) {demonmoves=10;}
s = new int[xmax][ymax];
xx = new int[xmax][ymax];
yy = new int[xmax][ymax];
message1 = "Press a key to begin!";
gameStage=STAGE_INIT;
myInit();
setBackground(Color.white);
addComponentListener(this);
addKeyListener(this);
componentResized(null); // Naughty hack
myUpdate(); repaint();
thread1=new Thread(this);
thread1.start();
}
public void stop() {acBackground.stop();} // Okay, okay, it REALLY DOES get annoying!
public void destroy() {acBackground.stop();}
public void myScramble() {
final int keylist[] = {KeyEvent.VK_RIGHT, KeyEvent.VK_UP,
KeyEvent.VK_LEFT, KeyEvent.VK_DOWN};
KeyEvent keyEvent1;
int keyCode1,demonmove,dir;
dir=(int)(Math.random()*4);
for (demonmove=0; demonmove0) {hxnew=hx-1; hynew=hy; flag=true;}
break;
case KeyEvent.VK_DOWN:
if (hy0) {hxnew=hx; hynew=hy-1; flag=true;}
break;
}
if (flag) {
int iTemp;
playermove++;
message1="The demon made "+demonmoves+" moves. You have made "+playermove+" moves.";
acMove.play(); // stupid sound is delayed
iTemp=xx[hx][hy]; xx[hx][hy]=xx[hxnew][hynew]; xx[hxnew][hynew]=iTemp;
iTemp=yy[hx][hy]; yy[hx][hy]=yy[hxnew][hynew]; yy[hxnew][hynew]=iTemp;
s[hx][hy] = s[hxnew][hynew];
s[hxnew][hynew] = 0;
hx=hxnew;
hy=hynew;
}
}
public void keyReleased (KeyEvent e) {}
public void keyTyped (KeyEvent e) {
if (gameStage==STAGE_INIT || gameStage==STAGE_WON) {
gameStage=STAGE_SCRAMBLING; message1="Scrambling ..."; myScramble();
gameStage=STAGE_PLAYING; message1="Good luck!"; playermove=0; acBackground.loop();
return;
}
}
}
/*
Robot robot1;
try{
robot1 = new Robot();
robot1.keyPress(KeyEvent.VK_RIGHT);
} catch (Exception e) {System.err.println ("No robots allowed!");}
*/