
import java.awt.*;
import java.util.*;

public class avoiding extends BufferedApplet
{
   int N = 6, w = 0, h, n = -1;
   Thing thing[] = new Thing[N];
   Random rand = new Random();

   public void render(Graphics g) {

      // INITIALIZE FIRST TIME

      if (w == 0) {
	 w = bounds().width;
	 h = bounds().height;

         for (int i = 0 ; i < N ; i++) {
	    thing[i] = new Thing();
	    thing[i].id = i;
	    thing[i].x = w/2 + (rand.nextInt() % (w/2));
	    thing[i].y = h/2 + (rand.nextInt() % (h/2));
	    thing[i].angle = rand.nextDouble() * 2*Math.PI;
	    thing[i].R = w / 20;
         }
      }

      // CLEAR SCREEN

      g.setColor(Thing.RedBg);
      g.fillRect(0,0,w/3+1,h);
      g.setColor(Thing.GrnBg);
      g.fillRect(w/3,0,w/3+1,h);
      g.setColor(Thing.BluBg);
      g.fillRect(2*w/3,0,w/3+1,h);

      // DRAW THE VEHICLES

      for (int i = 0 ; i < N ; i++) {
	 Thing T = thing[i];
	 if (T.stopped) {
	    T.draw(g);
	    continue;
	 }

         double angle = T.angle;
	 double cos = Math.cos(angle), sin = -Math.sin(angle);

	 // TURN AWAY FROM WALLS

	 if (T.x <   3*T.R) T.angle += swerve(-cos, sin);
	 if (T.x > w-3*T.R) T.angle += swerve( cos,-sin);
	 if (T.y <   3*T.R) T.angle += swerve(-sin,-cos);
	 if (T.y > h-3*T.R) T.angle += swerve( sin, cos);

	 // TURN AWAY FROM OTHER VEHICLES

	 for (int j = 0 ; j < N ; j++)
	    if (j != i) {
	       Thing S = thing[j];
	       double dx = S.x - T.x, dy = S.y - T.y;
	       double r = Math.sqrt(dx*dx + dy*dy);
	       dx /= r;
	       dy /= r;
	       double t = 1 - r / (2 * (T.R + S.R));
               if (t > 0)
	          T.angle += 4 * t * swerve(dx*cos + dy*sin, dy*cos - dx*sin);
	    }

         // MOVE FORWARD

         double fwd = 3;
	 if (T.highlight = 3 * mx / w == i)
	    fwd *= 2 - 2. * my / h;
         T.x += (int)(fwd * cos);
         T.y += (int)(fwd * sin);

	 // HARD CONSTRAINT: STAY WITHIN ROOM

	 T.x = Math.min(T.x, w-T.R);
	 T.y = Math.min(T.y, h-T.R);
	 T.x = Math.max(T.x,   T.R);
	 T.y = Math.max(T.y,   T.R);

         // HARD CONSTRAINT: DON'T RUN INTO OTHER VEHICLES

         for (int j = 0 ; j < N ; j++)
	    if (j != i) {
	       Thing S = thing[j];
	       double dx = T.x - S.x, dy = T.y - S.y;
	       double dist = Math.sqrt(dx * dx + dy * dy);
	       double R = T.R + S.R;
	       if (dist < R) {
		  double f = (R - dist) / R;
		  double t = .5;
		  if (i == n || T.stopped) t = 0;
		  if (j == n || S.stopped) t = 1;
		  T.x +=    t  * dx * f;
		  T.y +=    t  * dy * f;
		  S.x -= (1-t) * dx * f;
		  S.y -= (1-t) * dy * f;
	       }
	    }

         T.draw(g);
      }

      // OUTLINE THE WINDOW

      g.setColor(Color.black);
      g.drawRect(0,0,w-1,h-1);

      animating = true; // FORCE CONTINUAL REFRESH

   }

   double swerve(double u, double v) { return .1 * (1 + u) * v; }

   int mx, my;
   public boolean mouseMove(Event e, int x, int y) {
      mx = x;
      my = y;
      return true;
   }

   boolean dragged = false;

   public boolean mouseDown(Event e, int x, int y) {
      dragged = false;
      n = -1;

      for (int i = N-1 ; i >= 0 ; i--) {
         int dx = x - thing[i].x;
         int dy = y - thing[i].y;
         int r = (int)Math.sqrt(dx * dx + dy * dy);
         if (r < thing[i].R) {
            thing[i].x = x;
            thing[i].y = y;
	    for (int j = 0 ; j < N ; j++)
	       thing[j].inDisk = i==j;
	    n = i;
	    break;
         }
      }
      damage = true;
      return true;
   }
   public boolean mouseDrag(Event e, int x, int y) {
      dragged = true;
      if (n >= 0) {
         thing[n].x = x;
         thing[n].y = y;
      }
      damage = true;
      return true;
   }
   public boolean mouseUp(Event e, int x, int y) {
      if (n >= 0) {
         thing[n].inDisk = thing[n].onEdge = false;
	 if (! dragged)
	    thing[n].stopped = ! thing[n].stopped;
      }
      damage = true;
      return true;
   }
   public boolean keyUp(Event e, int key) {
      switch (key) {
      case 'c':
	 damage = true;
      }
      return true;
   }
}

class Thing {
   static double dx[] = {.6, -.35, -.35};
   static double dy[] = { 0, -.35,  .35};
   static Color Red = new Color(255,128,128);
   static Color Grn = new Color(128,255,128);
   static Color Blu = new Color(128,128,255);
   static Color RedBg = new Color(240,200,200);
   static Color GrnBg = new Color(200,240,200);
   static Color BluBg = new Color(200,200,240);

   int X[] = {0,0,0}, Y[] = {0,0,0};
   int id, x, y, R;
   double angle = 0;
   boolean stopped = false, inDisk, onEdge, highlight = false;

   void draw(Graphics g) {
      switch (id) {
      case 0 : g.setColor(Red); break;
      case 1 : g.setColor(Grn); break;
      case 2 : g.setColor(Blu); break;
      default: g.setColor(Color.white);
      }
      g.fillOval(x - R, y - R, 2*R, 2*R);

      if (onEdge) {
         g.setColor(Color.red);
	 for (int i = -2 ; i <= 2 ; i++) {
	    int r = R + i;
            g.drawOval(x - r, y - r, 2*r, 2*r);
         }
      }
      else {
         g.setColor(Color.black);
         g.drawOval(x - R, y - R, 2*R, 2*R);
      }

      double c = Math.cos(angle), s = Math.sin(angle), r = highlight ? 1.4*R : R;
      for (int i = 0 ; i < X.length ; i++) {
         X[i] = (int)(x + r * ( dx[i] * c + dy[i] * s));
         Y[i] = (int)(y + r * (-dx[i] * s + dy[i] * c));
      }
      g.setColor(Color.black);
      g.fillPolygon(X,Y,X.length);
   }

   int type() { return id / 3; }
}

