// Sparrow in a swarm // // Author Matthew Caryl // Created 14.12.96 // // 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 ADT.Physical; import java.awt.Rectangle; import java.lang.Math; final class Sparrow extends Bot { // // Private interface // // global sparrow data polar coordinates private static final int radius = 5; private static final int collision_radius = radius + 1; private static final float forwardSpeed = 3; private static final float angularSpeed = PI / 30f / QUARTERPI; // classify distance as one particular range private int range(int d) { for (int i = 0; i < NUMBER_RANGES - 1; i++) if (d < ranges[i]) return i; return NUMBER_RANGES - 1; } // // Package interface of sparrow // // behavioural controls static int NUMBER_RANGES = 5; static int[] ranges = new int[NUMBER_RANGES]; static float[] alignment = new float[NUMBER_RANGES]; static float[] cohesion = new float[NUMBER_RANGES]; static float[] separation = new float[NUMBER_RANGES]; static float[] evasion = new float[NUMBER_RANGES]; static float[] avoidance = new float[NUMBER_RANGES]; static float[] normal = new float[NUMBER_RANGES]; // ranges go out at 5 body radius intervals static { for (int i = 0; i < NUMBER_RANGES; i++) { ranges[i] = sqr((int) ((float) (5 * radius * i) / (float) NUMBER_RANGES)); alignment[i] = 1f / 5f; cohesion[i] = alignment[i]; separation[i] = alignment[i]; evasion[i] = alignment[i]; avoidance[i] = alignment[i]; normal[i] = 0; } ranges[NUMBER_RANGES - 1] = 25600; // very big range } // remove sparrow from world void kill() { world.remove(this); } // // public interface // // retrieve outlines of sparrow components public int[][][] outline() { outline[0][0][0] = (int) (x + radius * Math.sin(heading + 0f)); outline[0][1][0] = (int) (y - radius * Math.cos(heading + 0f)); outline[0][0][1] = (int) (x + radius * Math.sin(heading + 2.36f)); outline[0][1][1] = (int) (y - radius * Math.cos(heading + 2.36f)); outline[0][0][2] = (int) (x + radius * Math.sin(heading + 3.93f)); outline[0][1][2] = (int) (y - radius * Math.cos(heading + 3.93f)); outline[0][0][3] = outline[0][0][0]; outline[0][1][3] = outline[0][1][0]; return outline; } // retrieve bounding rectangle of tank components public Rectangle bounds() { bounds.reshape((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius)); return bounds; } // retrieve the maximum radius of the sparrow public int radius() { return collision_radius; } // initialise sparrow public Sparrow(World W, int X, int Y) { world = W; x = X; y = Y; heading = (float) world.random().range(360) / (float) 180 * PI; color = randomColor(); } // clone sparrow public Sparrow(Sparrow s) { this(s.world, s.x(), s.y()); for (int i = MINIMUM_NEIGHBOURS - 1; i >= 0; i--) neighbours[i] = s.neighbours[i]; } // single move public void action() { remember(); // ready for collision int sparrowX = 0; int sparrowY = 0; float sparrowH = 0; int sparrow = 0; // scan neighbours for (int i = MINIMUM_NEIGHBOURS - 1; i >= 0; i--) { if (neighbours[i] instanceof Sparrow) { sparrowX += neighbours[i].x(); sparrowY += neighbours[i].y(); sparrowH += neighbours[i].heading(); sparrow++; } } if (sparrow > 0) { sparrowX /= (float) sparrow; sparrowY /= (float) sparrow; sparrowH /= (float) sparrow; // change heading based on behaviour float sh = angle(sparrowH); float sp = angle(sparrowX, sparrowY); float sc = angle(neighbours[0].x(), neighbours[0].y()); int rp = range(separation(sparrowX, sparrowY)); int rc = range(separation(neighbours[0].x(), neighbours[0].y())); heading += alignment[rp] * angularSpeed * sh; heading -= cohesion[rp] * angularSpeed * sp; heading += separation[rc] * angularSpeed * sc; } // check for kestrel anywhere in world Bot kestrel = world.kestrel(); if (kestrel != null) { int kestrelX = kestrel.x(); int kestrelY = kestrel.y(); float kestrelH = kestrel.heading(); // change heading based on behaviour float sh = angle(kestrelH); float sp = angle(kestrelX, kestrelY); int r = range(separation(kestrelX, kestrelY)); heading += evasion[r] * angularSpeed * angle(kestrelH + (sh > 0 ? -QUARTERPI : QUARTERPI)); // run at 90 degrees heading += avoidance[r] * angularSpeed * sp; } x += Math.sin(heading) * forwardSpeed; y -= Math.cos(heading) * forwardSpeed; normalise(); } }