80.JAVA编程思想——复杂性理论
下面要介绍的程序的前身是由Larry O‘Brien 原创的一些代码,并以由Craig Reynolds 于1986 年编制的“Boids”程序为基础,当时是为了演示复杂性理论的一个特殊问题,名为“凸显”(Emergence)。这儿要达到的目标是通过为每种动物都规定少许简单的规则,从而逼真地再现动物的群聚行为。每个动物都能看到看到整个环境以及环境中的其他动物,但它只与一系列附近的“群聚伙伴”打交道。动物的移动基于三个简单的引导行为:
(1) 分隔:避免本地群聚伙伴过于拥挤。
(2) 方向:遵从本地群聚伙伴的普遍方向。
(3) 聚合:朝本地群聚伙伴组的中心移动。
更复杂的模型甚至可以包括障碍物的因素,动物能预知和避免与障碍冲突的能力,所以它们能围绕环境中的固定物体自由活动。除此以外,动物也可能有自己的特殊目标,这也许会造成群体按特定的路径前进。为简化讨论,避免障碍以及目标搜寻的因素并未包括到这里建立的模型中。尽管计算机本身比较简陋,而且采用的规则也相当简单,但结果看起来是真实的。也就是说,相当逼真的行为从这个简单的模型中“凸显”出来了。
程序以合成到一起的应用程序/程序片的形式提供:
1 代码
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;
class Beast {
int
x, y,
// Screen position
currentSpeed;
// Pixels per second
float
currentDirection;//
Radians
Color color;
// Fill color
FieldOBeasts field;
// Where theBeast roams
static
finalintGSIZE= 10;
// Graphic size
public Beast(FieldOBeasts
f, intx,
int
y, float
cD, int
cS, Color c) {
field =
f;
this.x =
x;
this.y =
y;
currentDirection =
cD;
currentSpeed =
cS;
color =
c;
}
public
voidstep() {
// You move based on those within your sight:
Vector
seen = field.beastListInSector(this);
// If you‘re not out in front
if (seen.size() > 0) {
// Gather data on those you see
int
totalSpeed = 0;
float
totalBearing = 0.0f;
float
distanceToNearest = 100000.0f;
Beast nearestBeast = (Beast)
seen.elementAt(0);
Enumeration
e = seen.elements();
while (e.hasMoreElements()) {
Beast
aBeast = (Beast) e.nextElement();
totalSpeed +=
aBeast.currentSpeed;
float
bearing = aBeast.bearingFromPointAlongAxis(x,
y, currentDirection);
totalBearing +=
bearing;
float
distanceToBeast =
aBeast.distanceFromPoint(x,
y);
if (distanceToBeast <
distanceToNearest){
nearestBeast =
aBeast;
distanceToNearest =
distanceToBeast;
}
}
// Rule 1: Match average speed of those
// in the list:
currentSpeed =
totalSpeed / seen.size();
// Rule 2: Move towards the perceived
// center of gravity of the herd:
currentDirection =
totalBearing / seen.size();
// Rule 3: Maintain a minimum distance
// from those around you:
if (distanceToNearest <=
field.minimumDistance){
currentDirection =
nearestBeast.currentDirection;
currentSpeed =
nearestBeast.currentSpeed;
if (currentSpeed >
field.maxSpeed){
currentSpeed =
field.maxSpeed;
}
}
} else {
// You are infront, so slow down
currentSpeed = (int) (currentSpeed *
field.decayRate);
}
// Make the beast move:
x += (int) (Math.cos(currentDirection) *
currentSpeed);
y += (int) (Math.sin(currentDirection) *
currentSpeed);
x %=
field.xExtent;
y %=
field.yExtent;
if (x < 0)
x +=
field.xExtent;
if (y < 0)
y +=
field.yExtent;
}
public
floatbearingFromPointAlongAxis(int
originX,intoriginY,floataxis){
// Returns bearing angle of the current Beast
// in the world
coordiante system
try {
double
bearingInRadians = Math.atan((this.y -
originY) / (this.x -
originX));
// Inverse tan has two solutions, so you
// have to correct for other quarters:
if (x <
originX){
if (y <
originY){
bearingInRadians += -(float) Math.PI;
} else {
bearingInRadians = (float) Math.PI -
bearingInRadians;
}
}
// Just subtract the axis (in
radians):
return (float) (axis -
bearingInRadians);
} catch (ArithmeticException
aE) {
// Divide by 0 error possible on this
if (x >
originX){
return 0;
} else
return (float) Math.PI;
}
}
public
floatdistanceFromPoint(int
x1,inty1){
return (float) Math.sqrt(Math.pow(x1 -
x, 2) + Math.pow(y1 -
y, 2));
}
public Point position() {
return
new Point(x,
y);
}
// Beasts knowhow to draw themselves:
public
voiddraw(Graphics
g) {
g.setColor(color);
int
directionInDegrees = (int)((currentDirection* 360) / (2 * Math.PI));
int
startAngle = directionInDegrees- FieldOBeasts.halfFieldOfView;
int
endAngle = 90;
g.fillArc(x,
y, GSIZE,GSIZE,startAngle,endAngle);
}
}
public
class FieldOBeasts extends Applet
implements Runnable {
private
Vector beasts;
static
floatfieldOfView= (float)(Math.PI/ 4),
// In radians
// Deceleration % per second:
decayRate = 1.0f,
minimumDistance= 10f;
// In pixels
static
inthalfFieldOfView = (int)((fieldOfView* 360) / (2 * Math.PI)),
xExtent= 0, yExtent= 0,
numBeasts= 50,
maxSpeed = 20;
// Pixels/second
boolean
uniqueColors= true;
Thread thisThread;
int
delay= 25;
public
voidinit() {
if (xExtent == 0 &&
yExtent == 0) {
xExtent = Integer.parseInt(getParameter("xExtent"));
yExtent = Integer.parseInt(getParameter("yExtent"));
}
beasts = makeBeastVector(numBeasts,
uniqueColors);
// Now start the beasts a-rovin‘:
thisThread =
new Thread(this);
thisThread.start();
}
public
voidrun() {
while (true) {
for (int
i = 0; i <
beasts.size(); i++) {
Beast
b = (Beast) beasts.elementAt(i);
b.step();
}
try {
thisThread.sleep(delay);
} catch (InterruptedException
ex) {
}
repaint();
// Otherwise it won‘t update
}
}
Vector makeBeastVector(int
quantity, boolean
uniqueColors) {
Vector
newBeasts = new Vector();
Random generator =
new Random();
// Used only if uniqueColors is on:
double
cubeRootOfBeastNumber = Math.pow((double)
numBeasts, 1.0 / 3.0);
float
colorCubeStepSize = (float) (1.0 /
cubeRootOfBeastNumber);
float
r = 0.0f;
float
g = 0.0f;
float
b = 0.0f;
for (int
i = 0; i <
quantity; i++) {
int
x = (int)(generator.nextFloat()*
xExtent);
if (x >
xExtent - Beast.GSIZE)
x -= Beast.GSIZE;
int
y = (int)(generator.nextFloat()*
yExtent);
if (y >
yExtent - Beast.GSIZE)
y -= Beast.GSIZE;
float
direction = (float) (generator.nextFloat() * 2 * Math.PI);
int
speed = (int)(generator.nextFloat()* (float)maxSpeed);
if (uniqueColors) {
r +=
colorCubeStepSize;
if (r > 1.0) {
r -= 1.0f;
g +=
colorCubeStepSize;
if (g > 1.0) {
g -= 1.0f;
b += colorCubeStepSize;
if (b > 1.0)
b -= 1.0f;
}
}
}
newBeasts.addElement(new Beast(this,
x, y,
direction,speed,newColor(r,g,b)));
}
return
newBeasts;
}
public
Vector beastListInSector(Beast viewer) {
Vector
output = new Vector();
Enumeration
e = beasts.elements();
Beast aBeast = (Beast)
beasts.elementAt(0);
int
counter = 0;
while (e.hasMoreElements()) {
aBeast = (Beast)
e.nextElement();
if (aBeast !=
viewer){
Point p =
aBeast.position();
Point
v = viewer.position();
float
bearing = aBeast.bearingFromPointAlongAxis(v.x,
v.y,
viewer.currentDirection);
if (Math.abs(bearing) <
fieldOfView / 2)
output.addElement(aBeast);
}
}
return
output;
}
public
voidpaint(Graphics
g){
Enumeration
e = beasts.elements();
while (e.hasMoreElements()) {
((Beast)
e.nextElement()).draw(g);
}
}
public
staticvoidmain(String[]
args){
FieldOBeasts
field = new FieldOBeasts();
field.xExtent = 640;
field.yExtent = 480;
Frame frame =
new Frame("Field ‘O Beasts");
// Optionally use a command-line argument
// for the sleep time:
if (args.length>= 1)
field.delay = Integer.parseInt(args[0]);
frame.addWindowListener(new WindowAdapter() {
public
void windowClosing(WindowEvent
e) {
System.exit(0);
}
});
frame.add(field, BorderLayout.CENTER);
frame.setSize(640, 480);
field.init();
field.start();
frame.setVisible(true);
}
} /// :~
尽管这并非对Craig Reynold 的“Boids”例子中的行为完美重现,但它却展现出了自己独有的迷人之外。通过对数字进行调整,即可进行全面的修改。
2 总结
通过学习,大家知道运用Java 可做到一些较复杂的事情。通过这些例子亦可看出,尽管Java 必定有自己的局限,但受那些局限影响的主要是性能(比如写好文字处理程序后,会发现C++的版本要快得多——这部分是由于IO 库做得不完善造成的;而在你读到本书的时候,情况也许已发生了变化。但Java 的局限也仅此而已,它在语言表达方面的能力是无以伦比的。利用Java,几乎可以表达出我们想得到的任何事情。而与此同时,Java 在表达的方便性和易读性上,也做足了功夫。所以在使用Java
时,一般不会陷入其他语言常见的那种复杂境地。使用那些语言时,会感觉它们象一个爱唠叨的老太婆,哪有Java 那样清纯、简练!而且通过Java 1.2 的JFC/Swing 库,AWT 的表达能力和易用性甚至又得到了进一步的增强。