import processing.core.*; 
import processing.xml.*; 

import java.util.*; 
import processing.*; 
import processing.core.*; 

import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 



// --------------------------------------------------------------------

//Braidenberg Simulation of Zeno\u2019s Paradox
//Achilles vs. the Evil Psychic Tortoise
//By Andrew Quitmeyer


//Based on: Braitenberg Vehicles Program
//
    // Based on program written by William Ngan <contact@metaphorical.net>
    // And on code by Michael Mateas, Mayhew Seavey, Jason Alderman 2004/05
    // Revised by Ali Mazalek, Fall 2006
// --------------------------------------------------------------------
// 

//
//Click and drag to fire \u201cFlecther\u2019s Paradox\u201d arrows!
//Press up on the keyboard if you want to add more Achillesbots (not technically supported since doesn\u2019t make much sense in terms of the original story, and too many achilles makes it go SLOOOOOWWW\u2026but it is fun!)
//
//---------------------------------------------------------------------------------------
//In this project I sought to recreate Zeno\u2019s famous paradox within the realm of virtual braitenberg vehicles.  Inspiration came from Wallace\u2019s \u201cEverything and More: A Compact History of Infinity\u201d and moreso from the fun, repeated use of the Tortoise and Achilles characters in Hofstadter\u2019s \u201cGodel, Escher, Bach.\u201d In case the reader is not familiar with Zeno\u2019s paradox, the idea runs mostly as follows:
//
//Achilles, the famous greek hero, was one day challenged to a race by a clever tortoise. The tortoise stipulated that if he was only given a small head start, he would be sure to win the race. The tortoise went on to logically \u201cprove\u201d the outcome of the race by stating that no matter how much faster achilles may be than the tortoise, by the time that achilles reached the starting position fo the tortoise, the tortoise will also have moved some distance, and by the time that achilles reaches the tortoise\u2019s new position, the tortoise will have moved once more. The idea is that no matter how infintesimal the distance moved by the tortoise, since he had a head start he will remain in the lead. Achille\u2019s, no match for the wit of the tortoise, then concedes the match to the seemingly inevitable winner, the Tortoise.
//
//This paradox is often rephrased in the following manner:
//If Achilles wants to reach the finish line, he must first move half the distance to the goal. After that, he would have to traverse half of the remaining distance... and half of the distance after that- ad infinitum. Consequently, Achilles would approach the goal but never be able to actually reach it. Obviously there is something weird about this logical puzzle since we have all been able to arrive at places instead of just infinitely approaching locations. In fact, if this logic were true it would disprove motion altogether since, before Achilles could even reach the halfway point, he would have to  make it halfway between the starting line and the halfway point, and before he could get there he would need to make it halfway between the start and the \u00bc mark\u2026
//
//Difficulties in simulation:
//The two largest difficulties encountered in trying to simulate this contest of crazy logic versus common sense were 1) designing a program to realistically function yet depict an impossible, purely mental behavior, and 2) performance issues with the base Braitenberg code.
//
//1.	Creating the paradox:
//It was really hard to think of a depiction of the paradox that would also be interesting. Since the paradox technically disproves motion one way to simulate it would be to have all the characters remain motionless, and Achilles say, \u201cYou win\u201d to the tortoise. Not too interesting. Alternately we could have Achilles just go straight for the goal but stop infinitesimally close to it. Again, boring.  In order for this project to be interesting at all there were going to have to be times that Achilles did not obey this crazy logic along with times that he did obey it. Thus I derived the idea of the evil, psychic tortoise with the ability of implanting these crazy thoughts in the impressionable Achilles head, but the range at which he could implant these thoughts was limited. Therefore there would be a gradient constructed with which Achilles behavior was driven. If he got too close to the tortoise his confusion would max out and he would be almost entirely driven by the tortoise\u2019s logic, and if the tortoise were far away Achilles would behave with his normal, common sense.
//
//This is accomplished by having 3 interconnected, sensory fields, one representing the real goal, one displaying mental goals implanted by the tortoise, and one that implements the tortoise\u2019s psychic influence. Here\u2019s how it works:
//Both Achilles and the Tortoise are modded, loverbots which seek out bright sources, with the addition that they have the addition of a constant translational velocity.
//The tortoise, unhindered by his own crazy logic, constantly seeks and moves toward the true goal, while broadcasting crazy waves of influence. Achilles will constantly seek out and move toward the true goal at a speed 10 times greater than the tortoise (preserving the 10:1 ratio of the original paradox) unless he falls within the tortoise\u2019s mind rays. In that case the tortoise will place a faux goal on the ground located halfway between Achilles and the true goal (he whispers to him mentally, \u201cWell if you want to get to that goal, first you have to go halfway!\u201d ). The intensity of these pseudo-goals is created by how much influence the tortoise had over Achilles at the time of the fake goal\u2019s creation. Thus, if the tortoise is far away, the fake goal may seem dim in comparison to the real goal, but if Achilles is being bombarded by this mental radiation the intensity of the fake source (and Achilles\u2019s confusion) will be so great that he knows, no matter what, before he reaches the real goal he definitely must go halfway!
//To spice things up even more, some other features were added:
//Perhaps you are sick of that tortoise lording it over Achilles and you want to mess with the outcome of the simulation. Well you can click and drag your mouse to fire arrows from the related \u201cFletcher\u2019s Paradox\u201d (not true Fletcher\u2019s Paradox arrows since that deals with the infinite division of time as opposed to space). If these arrows hit the tortoise right his mental powers will be temporarily diminished (his score is protected by his thick shell), but the arrows are also affected by the tortoise\u2019s mental abilities (they are modified explorer bots) and the arrows have to be right on target or they will slow down as the tortoise proves their motion impossible and eventually deflect.
//
//Achilles is super strong even if his mind is weak, so he can go very fast and shove the tortoise around if he needs to. He does have one other weakness however (let\u2019s call it his \u201cAchilles heel\u201d if you will). If Achilles gets hit in a tiny, tiny area near his back (around his heel?) he will lose all the points he had gotten thus far and be temporarily physically disabled.
//The program is also set up to add more Achilles by pressing \u201cUP\u201d on the keyboard. This feature isn\u2019t technically supported since it violates the original story and if you add too many Achilles, it drastically reduces the performance.
//
//2.	 Performance issues
//The original Braitenberg code is not really meant to have constantly moving light sources so other efforts were made to keep the program going at a good clip. I think it actually runs pretty super well even with up to 8 or so Achilles battling it out at the same time.

// --------------------------------------------------------------------










public class ZenosBraitenberg extends PApplet {

  // some useful constants
  static final int MAXLIGHTS = 10;
  static final int MAXVEHICLES = 50;
  static final int BLOCKSIZE = 1;  // resolution of our sensory field

  // sensory field
  SensoryField lightfield;
  SensoryField tortfield;
  SensoryField fauxfield;
  Source dragged = null; // a source that is being dragged
  Source vehicletag=null;
  Source vehicletagF=null;
  Source[] tortsources=new Source[MAXVEHICLES];
  ArrayList vehicles = new ArrayList();
  char mode = 'a'; // sets the type of vehicle that will be created
  PFont myfont;
  int Achscore=0;
  int Tortscore=0;
  float count=0;
  float achcount=0;
  boolean firsthit=false;
  float addgrow=500;
  PImage arrow, ach, tort, question;
  float achx, achy;
  float achAng=0;
  float tortamt=0;
  int xdown, xup, ydown, yup;
  PImage mindWave, goals;
  int arrowAng;
  // Processing setup
  // -----------------------------
  public void setup() {
    size(500,500);
    frameRate(20);
    ellipseMode(CENTER);
    rectMode(CENTER);
    noStroke();
    myfont= loadFont("Talie-48.vlw");
    textFont(myfont, 32);
    mindWave= loadImage("MindRing.png");
    arrow= loadImage("arrow.png");
    tort= loadImage("tort.png");
    ach= loadImage("ach.png");
    question= loadImage("question.png");
    goals= loadImage("goals.png");
    // create a light field
    lightfield = new SensoryField(width,height,BLOCKSIZE*2, 0);
    tortfield = new SensoryField(width,height,BLOCKSIZE*10, 130);
    fauxfield = new SensoryField(width,height,BLOCKSIZE*2, 400);
    for (int i=0; i<1; i++) {
      lightfield.addSource(new Source(random(10,width-10),random(10,height-90),500,0));

    }

    Source  solp = (Source) lightfield.getSource(0); 
    fauxfield.addSource(new Source(solp.x,solp.y,500,0));

    vehicles.add(new VehicleTort(this,90,90,random(TWO_PI),30, tort));
    vehicles.add(new VehicleAchilles(this,random(width),random(height),random(TWO_PI),10, ach));


    tortfield.addSource(new Source(90,90,500,100));  
    // smooth drawing
    smooth();


    //start with  light on

    // toggle light field visibility
    lightfield.setVisible(!lightfield.getVisible());
    //tortfield.setVisible(!tortfield.getVisible());
    fauxfield.setVisible(!fauxfield.getVisible());
    boolean showing = lightfield.getVisible();
    if (showing) {
      // only need to update ground if we've gone from hidden to visible
      lightfield.updateGround(this);
      //tortfield.updateGround(this);
      fauxfield.updateGround(this);
    }
    //      tortfield.updateGround(this);

    tortfield.setDrawRanges(!tortfield.getDrawRanges());
    println("source range visibility set to "+tortfield.getVisible());
  }

  // Processing draw
  // -----------------------------
  public void draw() {
    background(0);

    // draw the sensory field
    tint(255,80);


    fauxfield.drawMe(this);
    lightfield.drawMe(this);
    tortfield.drawMe(this);
    fill(40,230,20);
    textAlign(LEFT);
    textSize(32);
    text("Achilles\nTortoise", width/10, height*5/8);
    textAlign(RIGHT);
    text("score:"+Achscore+ "\nscore:"+Tortscore, width*9/10, height*5/8);

    // move and draw all the vehicles
    for (int i=0; i<vehicles.size(); i++) {
      Vehicle bv = (Vehicle)vehicles.get(i);
      if (achcount>millis()+1&&bv instanceof VehicleAchilles){
        achAng=achAng+.5f;
        bv.angle=achAng;

      }

      bv.moveMe(vehicles,lightfield,tortfield,fauxfield,width,height);
      bv.drawMe(this); 

      if(count<millis()&&firsthit==true){

        Source s = (Source) tortfield.getSource(0);
        s.maxrange=s.maxrange+1;
        addgrow=s.maxrange;
        //println(addgrow);
        if(s.maxrange>499){
          firsthit=false;
        }

      }



      if (bv instanceof VehicleArrow){
        VehicleArrow  va=(VehicleArrow)bv;
        if(bv.x<10||bv.x>width-10||bv.y<10||bv.y>height-80){
          vehicles.remove(i);

        }
        //if arrow hits achilles
        if(va.x>achx-5&&va.x<achx+5&&va.y<achy+20&&va.y>achy+10){
          vehicles.remove(i);
          Achscore=0;
          achcount=millis()+1000;
        }
        //if arrow hits tort
        if(va.tortsense<.13f){
          Source s = (Source) tortfield.getSource(0);
          println("HURT!");
          s.maxrange=constrain(s.maxrange-300,200,100000);
          // tortfield.updateAll(this); // remember to update the sensory field
          vehicles.remove(i);
          firsthit=true;
          count=millis()+1000;
          addgrow=s.maxrange;
        }


      }
      if (bv instanceof VehicleTort){ // && millis()%2==0

        Source s = (Source) tortfield.getSource(0);

        s.setLocation(width,height,bv.x,bv.y);
        tortfield.updateAll(this); // remember to update the sensory field
      }


      //See if a tortoise or an Achilles made it to the target first and then set another goal
      //   for (int p=0; p<lightfield.numSources(); p++) {
      Source s = (Source) lightfield.getSource(0);
      Source f = (Source) fauxfield.getSource(0);

      if (bv instanceof VehicleAchilles){
        //   println(bv.tAttack);
        achx=bv.x;
        achy=bv.y;
        achAng=bv.angle;
        tortamt= bv.sL.getSense(tortfield,false);

        if (s.hitTest(bv.x,bv.y,1)) {
          vehicletag=s;
          vehicletagF=f;
          Achscore++;
          if (vehicletag != null) {
            // if he tags a light source then move a new one it
            vehicletag.setLocation(width,height,random(width),random(height));
            vehicletagF.setLocation(width,height,vehicletag.x,vehicletag.y);
            lightfield.updateAll(this); 
            fauxfield.updateAll(this);// remember to update the sensory field
          }
        }
      }


      if (bv instanceof VehicleTort){



        imageMode(CENTER); 

        pushMatrix();

        translate(bv.x,bv.y);

        scale(1.1f*sin(millis()/500.0f)*(addgrow/500));

        image(mindWave,0,0);

        popMatrix();

        imageMode(CORNER);        
        if (s.hitTest(bv.x,bv.y,2)) {
          vehicletag=s;
          vehicletagF=f;
          Tortscore++;
          if (vehicletag != null) {
            // if we're holding a light source then drag it
            vehicletag.setLocation(width,height,random(width),random(height));
            vehicletagF.setLocation(width,height,vehicletag.x,vehicletag.y);
            lightfield.updateAll(this); // remember to update the sensory field
            fauxfield.updateAll(this);
          } 


        } 
      }

      pushMatrix();
      tint(255,255);
      imageMode(CENTER);
      translate(achx,achy);
      rotate(achAng+3*PI/2);
      scale(tortamt);
      rotate(PI/4);
      translate(15,-85);
      image(question,0,0);
      //text("?",0,0);

      popMatrix();
      //}
    }
    rectMode(CORNER);
    fill(40,230,40);
    rect(0,height-80, width, 80);
    textAlign(CENTER);
    fill(0);
    textSize(20);
    text("Braitenberg Vehicle Simulation\n of Zeno's Paradox", width/2, height-60);
    pushMatrix();
    translate(0, height-22);
    rotate(PI);
    image(ach, -30, 0, 35,35);

    image(tort,-170,-5,35,35);
    //image(arrow, -315,-10,15,55);
    image(goals, -315,0);
    popMatrix();
    textSize(18);
    textAlign(LEFT);
    text("Achilles", 45,height-16);
    text("Tortoise", 185,height-16);
    //textSize(14);
    text("True Goal",328,height-22);
    text("False Goal",330,height-5);
    //text("(Fletcher's Paradox)",315,height-5);
    //text("Arrow",320,height-16);
    //textSize(12);
    //text("(Fletcher's Paradox)",315,height-5);
    imageMode(CORNER);
  }

  // Mouse is used to move light sources
  // -----------------------------
  public void mousePressed() {
    xdown=mouseX;
    ydown=mouseY;

    for (int i=0; i<lightfield.numSources(); i++) {
      Source s = (Source) lightfield.getSource(i);
      if (s.hitTest(mouseX,mouseY,1)) {
        dragged = s; // we've clicked light source s 
        break;
      }
    }




  }

  // Mouse is used to move light sources
  // -----------------------------
  public void mouseDragged() {
    strokeWeight(4);
    stroke(200,20,20);
    line(xdown,ydown, mouseX,mouseY);
    if (dragged != null) {
      // if we're holding a light source then drag it
      dragged.setLocation(width,height,mouseX,mouseY);
      lightfield.updateAll(this); // remember to update the sensory field
    } 
  }

  // Drop our light source if we were holding one
  // -----------------------------
  public void mouseReleased() {
    xup=mouseX;
    yup=mouseY;


    println("adding an Arrow vehicle");
    vehicles.add(new VehicleArrow(this,xdown,ydown,(float)Math.atan2((-ydown+yup),(-xdown+xup)),4, arrow));


    if (dragged != null) {
      dragged = null;
      lightfield.updateAll(this); // remember to update the sensory field
    }
  }





  // Keyboard controls
  // -----------------------------
  public void keyPressed() {
    if (key == ' ') {
      // toggle light field visibility
      lightfield.setVisible(!lightfield.getVisible());
      //tortfield.setVisible(!tortfield.getVisible());
      fauxfield.setVisible(!fauxfield.getVisible());
      boolean showing = lightfield.getVisible();
      if (showing) {
        // only need to update ground if we've gone from hidden to visible
        lightfield.updateGround(this);
        //tortfield.updateGround(this);
        fauxfield.updateGround(this);
        //      tortfield.updateGround(this);
      }
      println("light field visibility set to "+showing);
    } 
    else if (key == 'z') {
      // toggle the drawing of the range circles around the sources
      tortfield.setDrawRanges(!tortfield.getDrawRanges());
      println("source range visibility set to "+tortfield.getVisible());
    }
    else if (key == 'a') {
      // UP key presses will now create aggressive vehicles
      mode = 'a';
      println("mode set to Achilles: press UP to add Achilles vehicles");
    } 
    else if (key == 'c') {
      // UP key presses will now create coward vehicles
      mode = 'c';
      println("mode set to coward: press UP to add coward vehicles");
    } 
    else if (key == 'e') {
      // UP key presses will now create explorer vehicles
      mode = 'e';
      println("mode set to explorer: press UP to add aggressive vehicles");
    } 
    else if (key == 'l') {
      // UP key presses will now create lover vehicles
      mode = 'l';
      println("mode set to aggressive: press UP to add lover vehicles");
    }
    else if (key == 't') {
      // UP key presses will now create lover vehicles
      mode = 't';
      println("mode set to Tortoise: press UP to add Tortoise vehicles");
    }
    if (key == CODED) {
      if (keyCode == RIGHT) {
        // add a new light source if we're not already at the limit
        int n = lightfield.sources.size();
        if (n < MAXLIGHTS) {
          lightfield.addSource(new Source(random(10,width-10),random(10,height-80),500,0));
          lightfield.updateAll(this); 
        }
      } 
      else if (keyCode == LEFT) {
        // remove a light source from the field if we're not already down to zero
        int n = lightfield.sources.size();
        if (n > 0) {
          lightfield.removeSource(lightfield.sources.size()-1);
          lightfield.updateAll(this);
        }
      } 
      else if (keyCode == UP) {
        // create a new vehicle based on the mode we're in if not already at max
        if (vehicles.size() < MAXVEHICLES) {
          switch(mode) {
          case 'a':
            println("adding an Achilles vehicle");
            vehicles.add(new VehicleAchilles(this,random(width),random(height),random(TWO_PI),10, ach));
            break;
          case 'c':
            println("adding a coward vehicle");
            vehicles.add(new VehicleCoward(this,random(width),random(height),random(TWO_PI),10, ach));
            break;
          case 'e':
            println("adding an explorer vehicle");
            vehicles.add(new VehicleExplorer(this,random(width),random(height),random(TWO_PI),10,ach));
            break;
          case 'l':
            println("adding a lover vehicle");
            vehicles.add(new VehicleLover(this,random(width),random(height),random(TWO_PI),10,ach));
            break;
          case 't':
            println("adding a Tortoise vehicle");
            vehicles.add(new VehicleTort(this,random(width),random(height),random(TWO_PI),30, tort));
            break;
          }
        } 
      }
      else if (keyCode == DOWN) {
        // remove a vehicle if we're not already down to zero
        if (vehicles.size() > 0) {
          vehicles.remove(vehicles.size()-1); 
        }
      }
    }
  }
}



