Issues with my anthill program

Hi,

I'm trying to create an anthills program with some interactions between ants but I've already got some bad issues with this project. I've just done something to create anthills and to make ants move but... it's just creating one anthill and ants are motionless... Thank you !

Arnaud

var fourmisParColonie,nombreDeFourmiliere;
var antsSystem = [];

function setup()
{   
    createCanvas(windowWidth,windowHeight);
    background(0);
    fourmisParColonie = 20;
    nombreDeFourmiliere = 4;

    for(i = 0; i < nombreDeFourmiliere; i++)
    {
        antsSystem[i] = new Anthill(fourmisParColonie);
    }
}

function draw()
{
    for(i = 0; i < nombreDeFourmiliere; i++)
    {
        antsSystem[i].displayAnthill();
    }
}

function Anthill(_fourmisParColonie)
{
    this.fourmisPC = _fourmisParColonie;
    this.antTab = [];
    this.fourmiliere = createVector(random(windowWidth),random(windowHeight));
    this.color = createVector(random(255),random(255),random(255));

    for(i = 0; i < this.fourmisPC; i++)
    {
        this.antTab[i] = new Ant(this.fourmiliere);//création de chaque fourmi avec indication de la position de sa fourmilière
    }
}

Anthill.prototype.displayAnthill = function()
{
    background(0);

    for(i = 0; i < this.fourmisPC; i++)
    {
        this.antTab[i].displayAnt(this.color);
    }
}

function Ant(_fourmiliere)
{
    this.lafourmilieredecetteAnt = createVector(_fourmiliere.x,_fourmiliere.y);
    this.positionAnt = createVector(this.lafourmilieredecetteAnt.x + random(-30,30),this.lafourmilieredecetteAnt.y + random(-30,30));//spawn fourmi autour de sa fourmilière
    this.destinationAnt = createVector(0,0);
    this.deltaDestinationAnt = createVector(0,0);
    this.timer1 = setInterval(this.definelookFor,random(200,5000));
    this.timer2 = setInterval(this.lookFor,20);
}

Ant.prototype.definelookFor = function() //nouvel objectif
{
    this.destinationAnt.x = random(-50,50);
    this.destinationAnt.y = random(-50,50);
    clearInterval(this.timer1);
    this.timer1 = setInterval(this.definelookFor,random(200,5000));
}

Ant.prototype.lookFor = function() //aller vers cet objectif
{
    this.vitesse = random(20);
    this.deltaDestinationAnt.x = this.destinationAnt.x*this.vitesse/2000;
    this.deltaDestinationAnt.y = this.destinationAnt.y*this.vitesse/2000;
    this.position.add(this.deltaDestinationAnt);
    clearInterval(this.timer2);
    this.timer2 = setInterval(this.lookFor,20);
}

Ant.prototype.displayAnt = function(_color)
{
    this.colorAnt = createVector(_color.x,_color.y,_color.z);
    fill(this.colorAnt.x,this.colorAnt.y,this.colorAnt.z);
    ellipse(this.positionAnt.x,this.positionAnt.y,10,10);
}

Answers

  • edited November 2014

    "Programming Questions" is for Java Mode only! Please edit and change it to "p5.js Programming Questions"! (%)
    In order to succeed easier, you should have 2 concepts:

    1. Some class to represent a single Ant unit.
    2. Some class to represent a colony of those Ant objects. That is: Anthill.

    You can see the concept above in this "WaveTrios" online example:
    http://studio.processingtogether.com/sp/pad/export/ro.9lTJ6qSlmCidk/latest

    In which there's a Wave class, plus another WaveTrio which creates 3 Wave objects.

    You can see it written in Java Mode, CoffeeScript Mode & P5.JS in the following forum link:
    http://forum.processing.org/two/discussion/5615/coding-noob-needs-help-how-do-i-draw-an-expanding-circle-that-triggers-with-a-mouse-click

  • Note : on dit une fourmi, pas une fourmis... Ce n'est pas un nom invariant ! ;-)

  • Thank you but I think that is what I've done here, no ? I have Anthill() which creates some Ant(). I really don't undersand why it is not working...

    PS: Sorry for the wrong section posting... PPS: @PhiLho ;)

  • I guess I've found the bug. In all of your for () loops, the < relational operator is "glued" to some variable name, like this: i<nombreDeFourmiliere.
    You merely needs to separate both w/ a space and voilà: i< nombreDeFourmiliere! =:)

  • Again thank you for your time ! I've tried to separate all my variable names of relational operators but sadly it still has the same result :(

  • edited November 2014

    In draw(), you call:
    antsSystem[i].displayAnthill();

    displayAnthill() calls:
    this.antTab[i].displayAnt(this.color);

    displayAnt() just... displays the ant. I see nowhere (in this chain of calls) where the coordinates of the ants are changed. That's why ants are motionless!

    PS.: I suggest to choose between French and English identifiers. And I suggest to choose English, as it will be easier for non-French to help you.

    [EDIT] Ah, I see the setInterval call. I am not familiar enough with P5.js to know if that's an usual way to do this. In classical Processing, we rather rely on the regular draw() calls to animate things.

    [Another EDIT] You define a positionAnt property, but you update a position prop. Joy of dynamic languages...

  • edited November 2014

    I've changed the variable names to have English identifiers. I think that the main problem is linked with scope :

    var antsPerAnthill,numberOfAnthills;
    var anthillTab = [];
    
    function setup()
    {   
        createCanvas(windowWidth,windowHeight);
        background(0);
        antsPerAnthill = 20;
        numberOfAnthills = 4;
    
        for(i = 0 ; i < numberOfAnthills ; i++)
        {
            anthillTab[i] = new Ants(antsPerAnthill);
        }
    }
    
    function draw()
    {
        for(i = 0 ; i < numberOfAnthills ; i++)
        {
            anthillTab[i].displayAnts();
        }
    }
    
    function Ants(_antsPerAnthill)
    {
        this.numberOfAnts = _antsPerAnthill;
        this.antTab = [];
        this.anthillPosition = createVector(random(windowWidth),random(windowHeight));
        this.color = createVector(random(255),random(255),random(255));
    
        for(i = 0 ; i < this.numberOfAnts ; i++)
        {
            this.antTab[i] = new Ant(this.anthillPosition);
        }
    }
    
    Ants.prototype.displayAnts = function()
    {
        background(0);
        for(i = 0 ; i < this.numberOfAnts ; i++)
        {
            this.antTab[i].displayAnt(this.color);
        }
    }
    
    function Ant(_anthillPosition)
    {
        var self = this;
        this.anthillPositionOfThisAnt = createVector(_anthillPosition.x,_anthillPosition.y);
        this.positionAnt = createVector(this.anthillPositionOfThisAnt.x + random(-30,30),this.anthillPositionOfThisAnt.y + random(-30,30));
        this.destinationAnt = createVector(0,0);
        this.deltaDestinationAnt = createVector(0,0);
        this.timer1 = setInterval(this.definelookFor.bind(this),random(200,2000));
        this.timer2 = setInterval(this.lookFor.bind(this),20);
    }
    
    Ant.prototype.definelookFor = function() 
    {
        var self = this;
        this.destinationAnt.x = random(-50,50);
        this.destinationAnt.y = random(-50,50);
        clearTimeout(this.timer1);
        this.timer1 = setTimeout(this.definelookFor.bind(this),random(200,2000));
    }
    
    Ant.prototype.lookFor = function() 
    {
        var self = this;
        this.vitesse = random(20);
        this.deltaDestinationAnt.x = this.destinationAnt.x*this.vitesse/2000;
        this.deltaDestinationAnt.y = this.destinationAnt.y*this.vitesse/2000;
        this.positionAnt.add(self.deltaDestinationAnt); 
        clearTimeout(this.timer2);
        this.timer2 = setTimeout(this.lookFor.bind(this),20);
    }
    
    Ant.prototype.displayAnt = function(_color)
    {
        this.colorAnt = createVector(_color.x,_color.y,_color.z);
        fill(this.colorAnt.x,this.colorAnt.y,this.colorAnt.z);
        ellipse(this.positionAnt.x,this.positionAnt.y,10,10);
    }
    

    Now ants are moving but I have just one anthill... I'm going to study this :

    var self = this

    &

    this.timer1 = setTimeout(this.definelookFor.bind(this),random(200,2000));
    

    &

    this.positionAnt.add(self.deltaDestinationAnt);

    Because I don't understand it well (someone helped me for it) and I think that it's my main misunderstanding in this project...

  • Yeah, the concept of "this" is messy in JS, and even though I have read on it several time, should I have to write serious JS again, I would need to read again on the topic!

  • edited November 2014

    Yeah, the concept of "this" is messy in JS, ...

    Yup, that is the main reason I've taken so much to learn JS! And I'm still struggling to advance on it! =P~

    Even though it's messy, more than 90% of times, this surprisingly behaves just like in Java.
    Trick is, the this inside some function corresponds to the most immediate reference which invoked it.

    For example in ref1.ref2.myMethod();, the this inside myMethod() is the object ref2 points to.
    Previous reference ref1 doesn't count on it. Only the most immediate reference does!

    Also, even though myMethod() happens to be a property of another object, its this will always be the invoker's reference, not the object "owner" of the property. That lies the danger! :-SS

    Although there's a Function method called bind() which clones the function it was called upon.
    But this time forever bound to a specific object reference, no matter the object which invokes it: \m/
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

    @PhiLho, you can read more about this operator at the link below: =:)
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

  • edited December 2014

    I've tried to run your latest attempt but got no apparent error message.
    So I've decided to reconstruct your sketch from the beginning, based on the original w/ both Ant & Anthill classes.

    Also removed all those complicated stuffs like setTimeout() & bind().
    Processing already invokes draw() at 60 FPS by default. We don't need other means in order to sync timers.
    Now redefine(), your former definelookFor(), is synced to frameCount + frameRate()'s FPS! :-bd

    Well, now it's running. Dunno whether it's the expected behavior. But you can at least carry on from there:

    /**
     * Anthills (v2.1)
     * by  ArnaudM (2014/Nov/30)
     * mod GoToLoop
     *
     * forum.processing.org/two/discussion/8380/
     * issues-with-my-anthill-program
     *
     * studio.processingtogether.com/sp/pad/export/ro.91A3Jfnrsldhp/latest
     */
    
    // Sketch's Global Constants:
    const COLONIES = 5, ANTS = 20, FPS = 60;
    
    // Sketch's Global Variables:
    const anthills = Array(COLONIES);
    var looping = true;
    
    // Sketch's User Function:
    function instantiateAnthills(ants) {
      for (var i = 0; i ^ COLONIES; anthills[i++] = new Anthill(ants));
    }
    
    // Sketch's Callback Functions:
    function setup() {
      createCanvas(windowWidth*.75, windowHeight*.75);
      ellipseMode(CENTER).frameRate(FPS).smooth().noStroke();
      instantiateAnthills(ANTS);
    }
    
    function draw() {
      background(0);
      for (var i = COLONIES; i--; anthills[i].action());
    }
    
    function keyTyped() {
      (looping ^= true)? loop() : noLoop();
    }
    
    function mousePressed() {
      mouseButton == LEFT? keyTyped() : instantiateAnthills(ANTS);
    }
    
    // Class Anthill's Constructor:
    function Anthill(qty) {
      this.ants = Array(qty);
    
      const r = random(050, 0400) | 0;
      const g = random(050, 0400) | 0;
      const b = random(050, 0400) | 0;
      this.color = [r, g, b];
    
      const howFar = Anthill.REGION;
      const offset = howFar + Ant.DIAM;
    
      const centerX = random(offset, width  - offset) | 0;
      const centerY = random(offset, height - offset) | 0;
      this.pos = createVector(centerX, centerY);
    
      for (var i = 0; i ^ qty; ++i) {
        var posX = centerX + random(-howFar, howFar) | 0;
        var posY = centerY + random(-howFar, howFar) | 0;
        this.ants[i] = new Ant(posX, posY);
      }
    }
    
    // Class Anthill's Constants:
    Anthill.REGION = 30, Anthill.FRONTIER = 4*Anthill.REGION;
    
    // Class Anthill's Method:
    Anthill.prototype.action = function() {
      fill(this.color);
      var i = this.ants.length;
      while (i--)  this.ants[i].action(this.pos);
    }
    
    // Class Ant's Constructor:
    function Ant(x, y) {
      this.pos = createVector(x, y), this.vel = createVector();
      this.targetFrame = frameCount;
    }
    
    // Class Ant's Constants:
    Ant.DIAM = 10, Ant.MAX_VEL = 2;
    Ant.MIN_FRAMES = .4*FPS, Ant.MAX_FRAMES = 4*FPS;
    
    // Class Ant's Methods:
    Ant.prototype.action = function(center) {
      frameCount >= this.targetFrame && this.redefine(center);
      this.update();
      this.display();
    }
    
    Ant.prototype.display = function() {
      ellipse(this.pos.x, this.pos.y, Ant.DIAM, Ant.DIAM);
    }
    
    Ant.prototype.update = function() {
      this.pos.add(this.vel);
    }
    
    Ant.prototype.redefine = function(hill) {
      const howFar = Anthill.FRONTIER, maxVel = Ant.MAX_VEL;
    
      const dx = random(-howFar, howFar), dy = random(-howFar, howFar);
      const sp = random(maxVel);
      const tf = random(Ant.MIN_FRAMES, Ant.MAX_FRAMES)/maxVel | 0;
    
      this.vel.set(hill).add(dx, dy).sub(this.pos).setMag(sp);
      this.targetFrame = frameCount + tf;
    }
    
  • edited November 2014

    Hello again! Decided to adapt the P5.JS "Anthills" sketch above to Java & JS Modes too. :bz
    So it's more evident how the way Java deals w/ classes translates to JS's prototype style! O:-)
    It can also be viewed online below running under Processing.JS's framework: B-)
    http://studio.processingtogether.com/sp/pad/export/ro.91A3Jfnrsldhp/latest

    /**
     * Anthills (v2.1)
     * by  ArnaudM (2014/Nov/30)
     * mod GoToLoop
     *
     * forum.processing.org/two/discussion/8380/
     * issues-with-my-anthill-program
     *
     * studio.processingtogether.com/sp/pad/export/ro.91A3Jfnrsldhp/latest
     */
    
    // Sketch's Global Constants:
    static final int COLONIES = 5, ANTS = 20, FPS = 60;
    
    // Sketch's Global Variables:
    final Anthill[] anthills = new Anthill[COLONIES];
    boolean isLooping = true;
    
    // Sketch's User Function:
    void instantiateAnthills(int ants) {
      for (int i = 0; i != COLONIES; anthills[i++] = new Anthill(ants));
    }
    
    // Sketch's Callback Functions:
    void setup() {
      size(1280, 800, JAVA2D);
    
      ellipseMode(CENTER);
      frameRate(FPS);
      smooth(4);
      noStroke();
    
      instantiateAnthills(ANTS);
    }
    
    void draw() {
      background(0);
      for (Anthill hill : anthills)  hill.action();
    }
    
    void keyTyped() {
      if (isLooping ^= true)  loop();
      else                  noLoop();
    }
    
    void mousePressed() {
      if (mouseButton == LEFT)  keyTyped();
      else       instantiateAnthills(ANTS);
    }
    
    final class Anthill {
      // Class Anthill's Constants:
      static final int REGION = 30, FRONTIER = 4*REGION;
    
      // Class Anthill's Instance Variables:
      final color colour = (int) random(#000000);
      final PVector pos;
      final Ant[] ants;
    
      // Class Anthill's Constructor:
      Anthill(int qty) {
        ants = new Ant[qty];
    
        int offset  = REGION + Ant.DIAM;
        int centerX = (int) random(offset, width  - offset);
        int centerY = (int) random(offset, height - offset);
        pos = new PVector(centerX, centerY);
    
        for (int i = 0; i != qty; ++i) {
          float posX = centerX + random(-REGION, REGION);
          float posY = centerY + random(-REGION, REGION);
          ants[i] = new Ant(posX, posY);
        }
      }
    
      // Class Anthill's Method:
      void action() {
        fill(colour);
        for (Ant a : ants)  a.action(pos);
      }
    }
    
    final class Ant {
      // Class Ant's Constants:
      static final int DIAM = 10, FPS = 60; // FPS needed here in JS Mode!
      static final float MAX_VEL = 2.0;
      static final int MIN_FRAMES = FPS>>1, MAX_FRAMES = 4*FPS;
    
      // Class Ant's Instance Variables:
      final PVector pos, vel = new PVector();
      int targetFrame = frameCount;
    
      // Class Ant's Constructor:
      Ant(float x, float y) {
        pos = new PVector(x, y);
      }
    
      // Class Ant's Methods:
      void action(PVector center) {
        if (frameCount >= targetFrame)  redefine(center);
        update();
        display();
      }
    
      void display() {
        ellipse(pos.x, pos.y, DIAM, DIAM);
      }
    
      void update() {
        pos.add(vel);
      }
    
      void redefine(PVector hill) {
        float dx = random(-Anthill.FRONTIER, Anthill.FRONTIER);
        float dy = random(-Anthill.FRONTIER, Anthill.FRONTIER);
        float sp = random(MAX_VEL);
    
        vel.set(hill);
        vel.add(dx, dy, 0);
        vel.sub(pos);
        vel.normalize(); // setMag() not available in PJS v1.4.1.
        vel.mult(sp);
    
        int tf = round(random(MIN_FRAMES, MAX_FRAMES)/MAX_VEL);
        targetFrame = frameCount + tf;
      }
    }
    
  • Woah ! Thank you, it's a very compact and clean sketch. I'll try to understand each line but I think I'll have some questions about it... It's clearly a better way to begin this project. Again, thank you and see you !

Sign In or Register to comment.