1  /*  Knight's tour applet with double buffering	*/
  2  /*  Ernest Sibert, July 1996			*/
  3  
  4  import java.awt.*;
  5  import java.io.*;
  6  import java.net.*;
  7  
  8  
  9  public class Knight extends java.applet.Applet implements Runnable
 10   { 
 11     public static final Font cfont = new Font("Helvetica", Font.BOLD, 18);
 12  
 13     static Image knight;		/* The knight icon (32x32) */
 14  
 15     int bsize =  8,	/* Board dimension (squares)	*/
 16         sqd   = 48,	/* Square dimension (pixels)	*/
 17         ktd   = 32,	/* Knight dimension (pixels)	*/
 18         ktin  =  8,	/* inset for Knight (pixels)	*/
 19         nxin,		/* x offset for numbers		*/
 20         nyin;		/* y offset for numbers		*/
 21  
 22     String tFile;	/* Name of file with tour	*/
 23  
 24     Thread kThread;		/* Runs the Knight	*/
 25  
 26     Color bg = Color.blue,	/* Standard background	*/
 27  	 fg = Color.white,	/* Standard foreground	*/
 28  	 sr = Color.red,	/* Red square		*/
 29  	 sb = Color.black;	/* Black square		*/
 30  
 31     int   kh = 0, kv = 0,	/* Knight position (squares)	*/
 32                   ki = 0;	/* Number of moves so far	*/
 33  
 34     int  tourLength,		/* Number of squares in the tour */
 35  	tourk     = 0;		/* Position in the tour		 */
 36     Point tour[];		/* The tour, defined on		 */
 37  				/*  [0..tourLength] (inclusive)  */
 38  
 39     String board[][];		/* The chess board	*/
 40  
 41  	/* Board coding:  null = not visited		*/
 42  	/*		  " 0" = Start of tour		*/
 43  	/*                " 3" = Position in tour	*/
 44          /*		  "12" = Position in tour	*/
 45  
 46     Image  iBuff;		/* Image buffer			*/
 47     Graphics gcx;		/* Graphics context for iBuff	*/
 48     boolean ready = false;	/* To synchronize startup	*/
 49  
 50  
 51     /* Initialize the applet.  The compilcations regarding the knight	*/
 52     /* icon result from the fact that getImage(...) returns without	*/
 53     /* actually loading the image, so that early drawings may use an	*/
 54     /* incomplete, even non-existant icon.  The method `prepareImage',	*/
 55     /* initiates loading and returns true if the image is complete and	*/
 56     /* ready to draw.							*/
 57  
 58     public void init()
 59      { String bgs, fgs;				/* For color parameters */
 60        knight = getImage(getCodeBase(), "Knight.gif");	/* Specify image */
 61        boolean kready = prepareImage(knight, this);	/* Start loading */
 62        board = new String[bsize][bsize];
 63        tFile = getParameter("tourfile");
 64        setFont(cfont);  setBackground(bg);  setForeground(fg);
 65        getAppletContext().showStatus("Loading the tour");
 66        board = new String[bsize][bsize];
 67        loadTour();
 68        FontMetrics fm = getFontMetrics(cfont);
 69        int sh = fm.getAscent();  int sw = fm.stringWidth("77");
 70        nxin = (sqd - sw)/2;  nyin = (sqd + sh)/2;
 71        iBuff = createImage(size().width, size().height);
 72        gcx = iBuff.getGraphics();
 73        gcx.setFont(cfont);
 74        getAppletContext().showStatus("Loading image from server....");
 75        while ( ! prepareImage(knight, this) ) pause(200);  /* Wait for icon */
 76        getAppletContext().showStatus("Ready");
 77        ready = true;			/* Initialization is finished */
 78      }
 79  
 80  
 81     /* Load the tour from file tFile.  The file has a sequence of int's	*/
 82     /* of the form  <tour_length> <x> <y> ... <x> <y>  giving the	*/
 83     /* sequence of knight positions.					*/
 84     /* Uses StreamTokenizer assuming file has nothing but numbers and	*/
 85     /* comments identified by `//' (to end of line).  Of course, there	*/
 86     /* can be anything at all after the data that's read.		*/
 87  
 88     void loadTour()
 89      { try
 90         { StreamTokenizer data =
 91  	   new StreamTokenizer(new BufferedInputStream(
 92  			new URL(getCodeBase(), tFile).openStream()));
 93  	 data.parseNumbers();  data.slashSlashComments(true);
 94  	 int tok = data.nextToken();  tourLength = (int) data.nval;
 95  	// System.out.println("tourLength = " + tourLength); /* Diag. */
 96  	 if ( tourLength >= 0 )
 97  	  { tour = new Point[tourLength + 1];
 98  	    for ( int i = 0; i <= tourLength;  i++ )
 99  	     { int x, y;
100  	       tok = data.nextToken();  x = (int) data.nval;
101  	       tok = data.nextToken();  y = (int) data.nval;
102  	      // System.out.println("Point: " + x + ", " + y); /* Diag. */
103  	       tour[i] = new Point(x, y);
104  	     }
105  	  }
106         }
107        catch (IOException ex)
108         { System.out.println("Couldn't read file for tour - "
109  			      + ex.getMessage());
110         }
111      }
112  
113     /* Delay for specified time (in msec.) */
114  
115     void pause(int delay)
116      { try { Thread.sleep(delay); }
117        catch (InterruptedException ex) { }
118      }
119  
120     /* Start process - The wait until ready and call to drawBoard have	*/
121     /* been moved here, since Netscape reported a security violation	*/
122     /* when these were at the beginning or `run'.  Appletviewer was	*/
123     /* happy with either version.					*/
124  
125     public void start()
126      { while ( !ready )	pause(500);		/* Wait until ready	*/
127        getAppletContext().showStatus("Drawing board");
128        drawBoard();
129        if ( kThread == null )
130         { kThread = new Thread(this);
131  	 kThread.start(); }
132      }
133  
134     /* Stop process  */
135  
136     public void stop()
137      { if ( kThread != null )
138         synchronized (iBuff)
139  	{ kThread.stop();  kThread = null; }
140      }
141  
142  
143     /* The tour procedure, runs in kThread.  The initial busy wait	*/
144     /* assures that start-up activity is completed.			*/
145  
146     public void run()
147      { int delay = 1200;				/* Time between moves	*/
148        repaint();				/* Paint initial state	*/
149        getAppletContext().showStatus("Off and running");
150        while ( ki < tourLength)			/* runs in kThread	*/
151         { /* Record current position: */
152  	 String seq = String.valueOf(ki);
153  	 if ( 0 <= ki && ki < 10 ) seq = " " + seq;
154  	 synchronized (iBuff)			/* Update state		*/
155  	  { board[kh][kv] = seq;
156  	    ki++;  Point next = tour[ki];	/* Next square		*/
157  	    kh = next.x;  kv = next.y;		/* New board coords	*/
158  	  }
159  	 pause(delay);
160  	 moveKnight();  repaint();		/* Move and Repaint 	*/
161         }
162      }
163  
164  
165     /* Move the knight to the next square in the buffer.  Must be */
166     /* called with ki >= 1 (knight has moved).			 */
167  
168     void moveKnight()
169      { Point z;  int i, j, xloc, yloc;		/* For square coordinates */
170        z = tour[ki - 1];  i = z.x;  j = z.y;	/* Cell left by knight	  */
171        xloc = sqd*i;  yloc = sqd*j;
172        String ks = board[i][j];
173        synchronized (iBuff)
174         { gcx.setColor( ((i+j)&1) == 0 ? sr : sb );
175  	 gcx.fillRect(xloc, yloc, sqd, sqd);
176  	 gcx.setColor(fg);
177  	 gcx.drawString(ks, xloc+nxin, yloc+nyin);	/* Sequence number */
178  	 gcx.drawImage(knight, sqd*kh + ktin, sqd*kv + ktin, this);  /* Kt */
179         }
180       }
181  
182  
183     public void update(Graphics g)		/* Flicker reduction	*/
184      { paint(g); }
185  
186  
187     public void repaint()
188      { paint(getGraphics()); }
189  
190  
191     public void paint(Graphics g)		/* Using buffer iBuff	*/
192      { if ( ready )
193          synchronized (iBuff) {g.drawImage(iBuff, 0, 0, this);}
194      }
195  
196  
197     /* Draw board in buffer from scratch using current knight position */
198  
199     void drawBoard()
200      { synchronized (iBuff)
201         { for ( int i = 0;  i < bsize;  i++ )
202  	  for ( int j = 0;  j < bsize;  j++ )
203  	   { int xloc = sqd*i,  yloc = sqd*j; 
204  	     gcx.setColor( ((i+j)&1) == 0 ? sr : sb );
205  	     gcx.fillRect(xloc, yloc, sqd, sqd);
206               String ks = board[i][j];
207  	     if ( ks != null )			/* Draw tour sequence	*/
208  	      { gcx.setColor(fg);
209  	        gcx.drawString(ks, xloc+nxin, yloc+nyin);
210  	      }
211  
212              }
213           gcx.drawImage(knight, sqd*kh + ktin, sqd*kv + ktin, this);
214         }
215      }
216  
217     public void destroy()
218      { gcx.dispose();  super.destroy(); }
219  
220   }