/* ***************************************************************************** * Compilation: javac-algs4 BoidSimulator.java * Execution: java-algs4 BoidSimuator * Dependencies: KdTreeST.java Boid.java Hawk.java * * Implementation of a boid simulator using the KdTreeST data type, * supplemented with the method nearest(Point2D p, int k). * * Note: This code is a bit hacked together. Apologies for any messy * code. Interactivity features and other tweaks by Evan Sparano (Fall 2013). * * Instructions for using the boid simulator: * Press "o" to zoom out. * Press "i" to zoom in. * Press "t" to track the center of mass of all boids. * Press "h" to track the hawk. * Press "m" to manually control the camera. * While in "manual" mode, use arrow keys to control camera movement. * **************************************************************************** */ import java.awt.event.KeyEvent; import edu.princeton.cs.algs4.Point2D; import edu.princeton.cs.algs4.Queue; import edu.princeton.cs.algs4.StdDraw; import edu.princeton.cs.algs4.StdRandom; public class BoidSimulator { // mode selection constants private static final char MANUAL_MODE = 'm'; private static final char TRACKING_MODE = 't'; private static final char HAWK_MODE = 'h'; // camera movement constants private static final double ZOOM_FACTOR = 1.1; private static final double CAMERA_SPEED = 0.05; private static char mode = TRACKING_MODE; // start in "tracking" mode private static Iterable lookUpBoids(KdTreeST bkd, Iterable points) { Queue values = new Queue(); for (Point2D p : points) { values.enqueue(bkd.get(p)); } return values; } public static void main(String[] args) { Hawk hawk = new Hawk(0.5, 0.3); int NUM_BOIDS = 1000; // Each boid tracks a number of nearest neighbors equal to FRIENDS int FRIENDS = 10; Boid[] boids = new Boid[NUM_BOIDS]; double radius = 0.5; double currentX = 0.5; double currentY = 0.5; // Generate random boids. for (int i = 0; i < NUM_BOIDS; i++) { double startX = StdRandom.uniform(); double startY = StdRandom.uniform(); double velX = (StdRandom.uniform() - 0.5)/1000; double velY = (StdRandom.uniform() - 0.5)/1000; boids[i] = new Boid(startX, startY, velX, velY); } StdDraw.enableDoubleBuffering(); while (true) { // process keyboard input if (StdDraw.isKeyPressed(KeyEvent.VK_I)) // press "i" to zoom in radius *= 1/ZOOM_FACTOR; if (StdDraw.isKeyPressed(KeyEvent.VK_O)) // press "o" to zoom out radius *= ZOOM_FACTOR; if (StdDraw.isKeyPressed(KeyEvent.VK_M)) // press "m" to enter mode = MANUAL_MODE; // "manual" mode if (StdDraw.isKeyPressed(KeyEvent.VK_H)) // press "h" to enter mode = HAWK_MODE; // "hawk" mode if (StdDraw.isKeyPressed(KeyEvent.VK_T)) // press "t" to enter mode = TRACKING_MODE; // "tracking" mode // scale pen radius relative to zoom StdDraw.setPenRadius(0.01*(0.5/radius)); StdDraw.setXscale(currentX - radius, currentX + radius); StdDraw.setYscale(currentY - radius, currentY + radius); // draw all boids and calculate their meanX and meanY double meanX = 0.0; double meanY = 0.0; for (int i = 0; i < NUM_BOIDS; i++) { meanX += boids[i].x()/NUM_BOIDS; meanY += boids[i].y()/NUM_BOIDS; boids[i].draw(); } // draw the hawk hawk.draw(); // follow center of mass in tracking mode if (mode == TRACKING_MODE) { currentX = meanX; currentY = meanY; } // allow user to control movement in manual mode else if (mode == MANUAL_MODE) { // press "up arrow" to pan upwards if (StdDraw.isKeyPressed(KeyEvent.VK_UP)) currentY += radius*CAMERA_SPEED; // press "down arrow" to pan downwards if (StdDraw.isKeyPressed(KeyEvent.VK_DOWN)) currentY -= radius*CAMERA_SPEED; // press "left arrow" to pan to the left if (StdDraw.isKeyPressed(KeyEvent.VK_LEFT)) currentX -= radius*CAMERA_SPEED; // press "right arrow" to pan to the right if (StdDraw.isKeyPressed(KeyEvent.VK_RIGHT)) currentX += radius*CAMERA_SPEED; } // follow hawk in hawk mode else if (mode == HAWK_MODE) { currentX = hawk.x(); currentY = hawk.y(); } // The entire KdTree must be rebuilt every frame. Since the boids // are random, we expect a roughly balanced tree, despite the // lack of balancing in KdTreeST. KdTreeST bkd = new KdTreeST(); for (int i = 0; i < NUM_BOIDS; i++) { bkd.put(boids[i].position(), boids[i]); } for (int i = 0; i < NUM_BOIDS; i++) { Iterable kNearestPoints = bkd.nearest(boids[i].position(), FRIENDS); Iterable kNearest = lookUpBoids(bkd, kNearestPoints); boids[i].updatePositionAndVelocity(kNearest, hawk); } // The hawk will chase the nearest boid. Boid closestBoid = bkd.get(bkd.nearest(hawk.position())); hawk.updatePositionAndVelocity(closestBoid); // hawk.updatePositionAndVelocity(bkd.nearest(new Boid(hawk.x(), hawk.y()))); StdDraw.show(); StdDraw.pause(20); StdDraw.clear(); } } }