Tutorials (back to the list of tutorials)

## Multi-Agent Algorithm: Inter-Class Interaction (requires iGeo version 7.4.0 or higher)

### Agents and Attractors

In this section, techniques to use interaction of different classes is shown. You can define a specific role to each of different classes to have composite behavior in multi-agent system.

To control a large number of agents, an attractor is often introduced. You define attractors to be located somewhere in the space and agents are oriented towards the location of the attractor.

```import processing.opengl.*;
import igeo.*;

void setup(){
size(480,360,IG.GL);
IG.duration(500);

for(int i=0; i < 3; i++){
new MyAttractor(IRand.pt(0, 0, 20, 100, 100, 20));
}
//agents in a matrix
for(int i=0; i < 10; i++){
for(int j=0; j < 10; j++){
new MyLineAgent(new IVec(i*10, j*10, 0),
new IVec(0,0,0.5)).clr(.5,i*0.1,j*0.1);
}
}
}

static class MyAttractor extends IAgent{
IVec pos;
IPoint point;

MyAttractor(IVec p){
pos = p;
point = new IPoint(pos).clr(1.0,0,0);
}
void update(){
// random walk
}
}

static class MyLineAgent extends IAgent{
IVec pos, dir;
MyAttractor attractor = null;
double minDist = -1;

MyLineAgent(IVec p, IVec d){
pos = p;
dir = d;
}

void interact(IDynamics agent){
//searching the closest attractor
if(agent instanceof MyAttractor){
MyAttractor attr = (MyAttractor)agent;
double dist = attr.pos.dist(pos);
//first attractor to check
if(attractor == null){
attractor = attr;
minDist = dist;
}
//if less than minimum, it's new minimum
else if(dist < minDist){
attractor = attr;
minDist = dist;
}
}
}

void update(){
//if any closest attractor found, attractor is not null
if(attractor!=null){
IVec dif = attractor.pos.dif(pos);
dif.len(dir.len());
dir = dif;
//reset attractor and minDist
attractor = null;
minDist = -1;
}

}
}
```

One important part of the code is the code to find the closest attractor out of multiple attractors in the space. This is a typical algorithm to search data which have a minimum or maximum value. Each time interact(IDynamics agent) is called, it checks if the new agent is closer than the minimum distance so far in minDist except for the first time to check when attractor is null.

### Simple Boundary of Agents

This example shows a way to define rectangular boundary on XY plane. MyBoundary class is containing the definition of the rectangular boundary. The boundary is defined by 4 values of xmin, ymin, xmax and ymax. The agents of MyHexAgent check the boundary inside the interact method if if it's about to go out of the boundary, it reflects the direction. Another note to add is that the class of MyHexAgent has the geometry creation method separated from the update method to have the code more organized.

```import processing.opengl.*;
import igeo.*;

void setup(){
size(480, 360, IG.GL);
IG.duration(1000);

new MyBoundary(-200,-200,200,200);
new MyHexAgent(new IVec(0,0,0), new IVec(0,10,0));
}

class MyBoundary extends IAgent{
double minx, maxx, miny, maxy;
MyBoundary(double x1, double y1, double x2, double y2){
minx = x1;
miny = y1;
maxx = x2;
maxy = y2;
IG.rect(new IVec(minx, miny, 0), maxx-minx, maxy-miny);
}
}

class MyHexAgent extends IAgent{
IVec pos, dir;
double depth=0, depthInc=0.5, hue=0;

MyHexAgent(IVec p, IVec d){ pos = p; dir = d; }

void interact(IDynamics agent){
if(agent instanceof MyBoundary){
MyBoundary boundary = (MyBoundary)agent;
//checking if next position is out of the boundary
IVec nextPos = pos.cp(dir);
if(nextPos.x < boundary.minx){
dir.ref(IG.xaxis); //reflect on x-plane
}
else if(nextPos.x > boundary.maxx){
dir.ref(IG.xaxis); //reflect on x-plane
}
if(nextPos.y < boundary.miny){
dir.ref(IG.yaxis); //reflect on y-plane
}
else if(nextPos.y > boundary.maxy){
dir.ref(IG.yaxis); //reflect on y-plane
}
}
}

void update(){
createHexGeometry();
//random shift of direction
if(IRand.percent(10)){ dir.rot(IG.zaxis,PI/3); }
else  if(IRand.percent(10)){ dir.rot(IG.zaxis,-PI/3); }
//random shift of height
if(IRandom.percent(10)){ depthInc *= -1; }
depth += depthInc;
hue += 0.002;
}

void createHexGeometry(){
//creating hexagonal extrusion
IVec[] cpts = new IVec[6];
for(int i=0; i < 6; i++){
cpts[i] =
}
IG.extrude(cpts, 1, true, depth).hsb(hue,1,1);
}
}
```

### Blocking Agents

The code below shows an example to have another class agents to block the line agents shown before, by adding one blocking class "LineBlockAgent" to the code of branching line agents shown at this section.

```import processing.opengl.*;
import igeo.*;

void setup(){
size(480, 360, IG.GL);
IG.duration(150);
new LineAgent(new IVec(0,0,0), new IVec(1,0,0));

for(int i=-1; i<=1; i+=2){
for(int j=-1; j<=1; j+=2){
for(int k=-1; k<=1; k+=2){
new LineBlockAgent(new IVec(40*i,40*j,40*k), 40);
}
}
}
}

static class LineBlockAgent extends IAgent{
IVec pos;

}

void interact(IDynamics agent){
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
lineAgent.del();
}
}
}

void update(){
}
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length

IVec pt1, pt2;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir){
pt1 = pt;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
// checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){
del();
}
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(0);
IVec dir = pt2.dif(pt1);

//rotation axis with random direction
IVec axis = IRandom.pt(-1,1).len(1);

if(IRandom.percent(50)){ //bend
new LineAgent(pt2, dir.dup().rot(axis,
IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(50)){ //bend the other way
new LineAgent(pt2, dir.dup().rot(axis,
-IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(90)){ //straight
new LineAgent(pt2, dir.dup());
}
}
}
}
```

(back to the list of tutorials)