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 }