工程
这里是一些留待你解决的更具挑战性的工程。【其中的某一些可能以后会作为本书的例子,所以有一些可能会从这里拿掉】
老鼠和迷宫
首先,创建一个黑板(cite reference?)对象用来记录信息。在这个特殊的黑板上画迷宫,并且用它来显示由老鼠探测出来的迷宫的结构信息。
现在创建一个真正的迷宫,这个对象只暴露它自身很少一部分的信息——针对给定的坐标,它会告诉你紧挨着给它四周是墙壁还是空地,但是不会有其它更多的信息。对于新手,可以从一个文本文件读入迷宫(数据),但是可以考虑从因特网上找一个构造迷宫的算法。无论如何,最后的结果都应该是这么一个对象,这个对象可以根据给定的坐标找出它周围的墙壁和空地。还有一点,它还必须提供这个迷宫的入口以供查询。
最后,创建迷宫探路的老鼠Rat类。每个老鼠都可以与黑板和迷宫打交道,它向黑板报告自己的当前信息,并且根据自己所处的位置向迷宫请求新的信息。不管怎样,每次老鼠碰到迷宫分支点需要做出决策的时候,它就创建一个新的线程探测每一个分支。每个老鼠由它自己的线程驱动,当它走近死胡同以后,它会先向黑板报告自己探测的最终结果,然后它会结束自己所在的线程。
最终目标是绘出迷宫的完整地图,但是你必须得决定最后的结束条件是顺其自然找到的还是由黑板来判定。
下面是Jeremy Meyer写的一个实现的例子:
//: projects:Maze.java
package projects;
import java.util.*;
import java.io.*;
import java.awt.*;
public class Maze extends Canvas {
private Vector lines; // a line is a char array
private int width = -1;
private int height = -1;
public static void main (String [] args)
throws IOException {
if (args.length < 1) {
System.out.println("Enter filename");
System.exit(0);
}
Maze m = new Maze();
m.load(args[0]);
Frame f = new Frame();
f.setSize(m.width*20, m.height*20);
f.add(m);
Rat r = new Rat(m, 0, 0);
f.setVisible(true);
}
public Maze() {
lines = new Vector();
setBackground(Color.lightGray);
}
synchronized public boolean
isEmptyXY(int x, int y) {
if (x < 0) x += width;
if (y < 0) y += height;
// Use mod arithmetic to bring rat in line:
byte[] by =
(byte[])(lines.elementAt(y%height));
return by[x%width]==' ';
}
synchronized public void
setXY(int x, int y, byte newByte) {
if (x < 0) x += width;
if (y < 0) y += height;
byte[] by =
(byte[])(lines.elementAt(y%height));
by[x%width] = newByte;
repaint();
}
public void
load(String filename) throws IOException {
String currentLine = null;
BufferedReader br = new BufferedReader(
new FileReader(filename));
for(currentLine = br.readLine();
currentLine != null;
currentLine = br.readLine()) {
lines.addElement(currentLine.getBytes());
if(width < 0 ||
currentLine.getBytes().length > width)
width = currentLine.getBytes().length;
}
height = lines.size();
br.close();
}
public void update(Graphics g) { paint(g); }
public void paint (Graphics g) {
int canvasHeight = this.getBounds().height;
int canvasWidth = this.getBounds().width;
if (height < 1 || width < 1)
return; // nothing to do
int width =
((byte[])(lines.elementAt(0))).length;
for (int y = 0; y < lines.size(); y++) {
byte[] b;
b = (byte[])(lines.elementAt(y));
for (int x = 0; x < width; x++) {
switch(b[x]) {
case ' ': // empty part of maze
g.setColor(Color.lightGray);
g.fillRect(
x*(canvasWidth/width),
y*(canvasHeight/height),
canvasWidth/width,
canvasHeight/height);
break;
case '*': // a wall
g.setColor(Color.darkGray);
g.fillRect(
x*(canvasWidth/width),
y*(canvasHeight/height),
(canvasWidth/width)-1,
(canvasHeight/height)-1);
break;
default: // must be rat
g.setColor(Color.red);
g.fillOval(x*(canvasWidth/width),
y*(canvasHeight/height),
canvasWidth/width,
canvasHeight/height);
break;
}
}
}
}
} ///:~
//: projects:Rat.java
package projects;
public class Rat {
static int ratCount = 0;
private Maze prison;
private int vertDir = 0;
private int horizDir = 0;
private int x,y;
private int myRatNo = 0;
public Rat(Maze maze, int xStart, int yStart) {
myRatNo = ratCount++;
System.out.println("Rat no." + myRatNo +
" ready to scurry.");
prison = maze;
x = xStart;
y = yStart;
prison.setXY(x,y, (byte)'R');
new Thread() {
public void run(){ scurry(); }
}.start();
}
public void scurry() {
// Try and maintain direction if possible.
// Horizontal backward
boolean ratCanMove = true;
while(ratCanMove) {
ratCanMove = false;
// South
if (prison.isEmptyXY(x, y + 1)) {
vertDir = 1; horizDir = 0;
ratCanMove = true;
}
// North
if (prison.isEmptyXY(x, y - 1))
if (ratCanMove)
new Rat(prison, x, y-1);
// Rat can move already, so give
// this choice to the next rat.
else {
vertDir = -1; horizDir = 0;
ratCanMove = true;
}
// West
if (prison.isEmptyXY(x-1, y))
if (ratCanMove)
new Rat(prison, x-1, y);
// Rat can move already, so give
// this choice to the next rat.
else {
vertDir = 0; horizDir = -1;
ratCanMove = true;
}
// East
if (prison.isEmptyXY(x+1, y))
if (ratCanMove)
new Rat(prison, x+1, y);
// Rat can move already, so give
// this choice to the next rat.
else {
vertDir = 0; horizDir = 1;
ratCanMove = true;
}
if (ratCanMove) { // Move original rat.
x += horizDir;
y += vertDir;
prison.setXY(x,y,(byte)'R');
} // If not then the rat will die.
try {
Thread.sleep(2000);
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("Rat no." + myRatNo +
" can't move..dying..aarrgggh.");
}
} ///:~
The maze initialization file:
//:! projects:Amaze.txt
* ** * * ** *
*** * ******* * ****
*** ***
***** ********** *****
* * * * ** ** * * * ** *
* * * * ** * * * * **
* ** * **
* ** * ** * ** * **
*** * *** ***** * *** **
* * * * * *
* ** * * * ** * *
///:~
其它关于迷宫的资源
关于创建迷宫算法的讨论以及实现它们的java源代码:
http://www.mazeworks.com/mazegen/mazegen.htm
关于碰撞检测算法的讨论和其它针对自主物理对象(autonomous physical objects)单个或群体运动行为的讨论:
http://www.red3d.com/cwr/steer/
XML修饰器
针对I/O读写器写一对修饰器(decorators)用来对XML编码(写修饰器)和解码(读修饰器)。