Python 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.

```add_library('igeo')

def setup() :
size(480,360,IG.GL)
IG.duration(500)

for i in range(3) :
MyAttractor(IRand.pt(0, 0, 20, 100, 100, 20))

#agents in a matrix
for i in range(10) :
for j in range(10) :
MyLineAgent(IVec(i*10,j*10,0),IVec(0,0,0.5)).clr(.5,i*0.1,j*0.1)

class MyAttractor(IAgent) :

def __init__(self, p) :
self.pos = p
self.point = IPoint(self.pos).clr(1.0,0,0)

def update(self) :
# random walk

class MyLineAgent(IAgent) :

def __init__(self, p, d) :
self.pos = p
self.dir = d
self.attractor = None

def interact(self, agents) :
#searching the closest attractor
minDist = -1
for agent in agents :
if isinstance(agent, MyAttractor) :
if agent is not self :
dist = agent.pos.dist(self.pos)
#first attractor to check
if self.attractor is None :
self.attractor = agent
minDist = dist
#if less than minimum, it's minimum
elif dist < minDist :
self.attractor = agent
minDist = dist

def update(self) :
#if any closest attractor found, attractor is not null
if self.attractor is not None :
dif = self.attractor.pos.dif(self.pos)
dif.len(self.dir.len())
self.dir = dif
#reset attractor and minDist
attractor = None

```

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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(1000)
MyBoundary(-200,-200,200,200)
MyHexAgent(IVec(0,0,0), IVec(0,10,0))

class MyBoundary(IAgent) :

def __init__(self, x1, y1, x2, y2) :
self.minx = x1
self.miny = y1
self.maxx = x2
self.maxy = y2
IG.rect(IVec(x1,y1,0), x2-x1, y2-y1)

class MyHexAgent(IAgent) :

def __init__(self, p, d) :
self.pos = p
self.dir = d
self.depth = 0
self.depthInc = 0.5
self.hue = 0

def interact(self, agents) :
for agent in agents :
if isinstance(agent, MyBoundary) :
#checking if next position is out of the boundary
nextPos = self.pos.cp(self.dir)
if nextPos.x() < agent.minx :
self.dir.ref(IG.xaxis) #reflect on x-plane
elif nextPos.x() > agent.maxx :
self.dir.ref(IG.xaxis) #reflect on x-plane
if nextPos.y() < agent.miny :
self.dir.ref(IG.yaxis) #reflect on y-plane
elif nextPos.y() > agent.maxy :
self.dir.ref(IG.yaxis) #reflect on y-plane

def update(self) :
self.createHexGeometry()
#random shift of direction
if IRand.pct(10) :
self.dir.rot(PI/3)
elif IRand.pct(10) :
self.dir.rot(-PI/3)
#random shift of height
if IRand.pct(10) :
self.depthInc *= -1
self.depth += self.depthInc
self.hue += 0.002

def createHexGeometry(self) :
#creating hexagonal extrusion
cpts = []
for i in range(6) :
IG.extrude(cpts, 1, True, self.depth).hsb(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(150)
LineAgent(IVec(0,0,0), IVec(1,0,0))

for i in range(-1,2,2) : # -1, 1
for j in range(-1,2,2) :
for k in range(-1,2,2) :
LineBlockAgent(IVec(40*i,40*j,40*k), 40)

class LineBlockAgent(IAgent) :

def __init__(self, p, rad) :
self.pos = p

def interact(self, agents) :
for agent in agents :
if isinstance(agent, LineAgent) :
if agent.pt2.dist(self.pos) < self.radius :
agent.del()

def update(self) :
if self.time()==0 :

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False
def interact(self, agents) :
for agent in agents :
if self.time() == 0 : #only in the first time
if isinstance(agent, LineAgent) :
if agent is not self :
# checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2).clr(0)
dir = self.pt2.dif(self.pt1)

#rotation axis with random direction
axis = IRand.pt(-1,1).len(1)

if IRand.pct(50) : #bend
LineAgent(self.pt2, dir.dup().rot(axis,IRand.get(PI/3,PI/3*2)))
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, dir.dup().rot(axis,-IRand.get(PI/3,PI/3*2)))
if IRand.pct(90) : #straight
LineAgent(self.pt2, dir.dup())
```

(back to the list of tutorials)