// Physical objects with spacial information, tick movement, and display systems. // // Author Matthew Caryl // Created 13.12.96 // Modified 27.2.01 // // Although under copywrite to the author (Matthew Caryl) this code can be copied and modified for non-commercial // purposes as long as any derivatives contain this condition. package Swarm; import java.awt.Rectangle; import java.awt.Graphics; import java.awt.Color; import ADT.Physical; public abstract class Bot implements Physical { // // Protected interface // // Maths routines protected static final float PI = (float) (Math.PI); protected static final float TWOPI = (float) (2f * Math.PI); protected static final float QUARTERPI = (float) (Math.PI / 4f); protected static final int sqr(int n) {return n * n;} // individual bot data protected float x, y; protected float heading; protected World world; protected float oldX, oldY; protected float oldHeading; protected Color color; protected final Bot[] neighbours = new Bot[MINIMUM_SWARM]; protected int farthest; // individual bot display data protected int[][][] outline = new int[1][2][4]; protected Rectangle bounds = new Rectangle(); // Limit all values to within certain ranges protected void normalise() { if (heading < 0) heading += TWOPI; if (heading >= TWOPI) heading -= TWOPI; } // Store values in old variables protected void remember() { oldX = x; oldY = y; oldHeading = heading; } // bubble sort good for few relatively unchanging neighbours -- only one pass protected void sort(int left, int right) { int d1, d2; d1 = separation(neighbours[left]); for (int i = left + 1; i <= right; i++) { d2 = separation(neighbours[i]); if (d2 < d1) { Bot swap = neighbours[i]; neighbours[i] = neighbours[i - 1]; neighbours[i - 1] = swap; } else d1 = d2; } } // angle between given angle and current heading protected float angle(float second) { float s = second - heading; if (s > PI) return s - TWOPI; else if (s < -PI) return s + TWOPI; else return s; } // angle to given position and current heading protected float angle(int X, int Y) { float a; if (x != X) a = (float) Math.atan((float) (x - X) / (float) (Y - y)); else a = y < Y ? 0 : PI; if (Y < y) a += PI; return angle(a); } // random colour tending towards brighter colours protected Color randomColor() { int[] colorlist = {world.random().range(64) + 191, world.random().range(64) + 128, world.random().range(64) + 64}; for (int i = 3; i > 0; i--) { int p = world.random().range(3); int q = world.random().range(3); int temporary; temporary = colorlist[p]; colorlist[p] = colorlist[q]; colorlist[q] = temporary; } return new Color(colorlist[0], colorlist[1], colorlist[2]); } // // Package interface // static final int MINIMUM_SWARM = 6; static final int MINIMUM_NEIGHBOURS = MINIMUM_SWARM - 1; // partial sort of neighbours void newNeighbours() { sort(0, MINIMUM_NEIGHBOURS - 1); farthest = separation(neighbours[MINIMUM_NEIGHBOURS - 1]); } // add new neighbours void addNeighbours(Bot[] bots) { for (int i = MINIMUM_NEIGHBOURS - 1; i >= 0; i--) neighbours[i] = bots[i]; sort(0, MINIMUM_NEIGHBOURS - 1); farthest = separation(neighbours[MINIMUM_NEIGHBOURS - 1]); } // add possible neighbour to edge void possibleNeighbour(Bot b) { int s = separation(b); if (s >= farthest) return; for (int i = MINIMUM_NEIGHBOURS - 1; i >= 0; i--) if (neighbours[i] == b) return; neighbours[MINIMUM_NEIGHBOURS - 1] = b; farthest = s; } // farthest distance of neighbour squared int farthest() { return farthest; } // distance to bot squared int separation(Bot b) { return sqr((int) b.x - (int) x) + sqr((int) b.y - (int) y); } // distance to position squared int separation(int X, int Y) { return sqr(X - (int) x) + sqr(Y - (int) y); } // // Public interface // public int x() { return (int) x; } public int y() { return (int) y; } // set coordintates and remember old public void set(int X, int Y) { remember(); x = X; y = Y; } // has physical moved or changed heading public boolean moved() { return (x != oldX) | (y != oldY) | (heading != oldHeading); } // current facing public float heading() { return heading; } // collision with wall, revert to old position and spin round public void collision(boolean first) { x = oldX; y = oldY; heading = oldHeading + PI; normalise(); } // collision with bot public void collision(Physical p, boolean first) { // do nothing } public void sensor() { // do nothing } // single move public void tick() { sensor(); action(); } // display public void paint(Graphics g) { outline(); g.setColor(color); g.drawPolygon(outline[0][0], outline[0][1], outline[0][0].length); } // erase relying on outline having stayed constant public void erase(Graphics g) { g.drawPolygon(outline[0][0], outline[0][1], outline[0][0].length); } }