Python Tutorials (back to the list of tutorials)

## Multi-Agent 2D Examples

### Multi-Agent 2D Example 4

The following example shows a line agent which turns when it finds another line agent in front of it. It turns into the parallel direction of the another agent and it turns only left, not right. In its interact method, the agent searches the closest other agent in front within the threshold range and measures the angle of the other agent. This angle is stored in frontAngle and this is used to rotate the agent in the update method.

```add_library('igeo')

def setup() :
size(480,360,IG.GL)
IRand.init(3)
IG.duration(700)
LineAgent(IG.v(-0.1,0,0), IG.v(1,0,0)).clr(0)
LineAgent(IG.v(500,-0.1,0), IG.v(0,1,0)).clr(0)
LineAgent(IG.v(500+0.1,500,0), IG.v(-1,0,0)).clr(0)
LineAgent(IG.v(0,500+0.1,0), IG.v(0,-1,0)).clr(0)

for i in range(10) :
LineAgent(IRand.pt(100,100,0,400,400,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr(0.4,0.4,0.4))

class LineAgent(IAgent) :
# static variable constants
length = 2
clearance = 1.99 #less than length
angleThreshold = PI/20
threshold = 10

def __init__(self, pt, dir) :
self.pt1 = pt
self.pt2 = pt.cp(dir.cp().len(LineAgent.length))
self.isColliding = False
self.angle = 0
self.minDist = 0
self.frontAngle = 0

def interact(self, agents) :
if self.time() == 0 : #only in the first time
self.minDist = -1 #reset
for agent in agents :
if self.isColliding :
return
if isinstance(agent, LineAgent) and agent is not self :
# checking clearance of end point
dist = agent.pt2.dist(self.pt2)
if dist < LineAgent.clearance :
self.isColliding=True
elif dist < LineAgent.threshold : #not colliding but close
#check if not the parent and in front and closest one
if not self.pt1.eq(agent.pt2) and \
self.pt2.dif(self.pt1).angle(agent.pt2.dif(self.pt1)) < LineAgent.angleThreshold and \
(self.minDist < 0 or dist < self.minDist) :
self.minDist = dist
self.frontAngle = self.pt2.dif(self.pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis)

def update(self) :
if self.time() == 0 :
if self.isColliding :
self.del()
else : #if not colliding
ICurve(self.pt1,self.pt2).clr(self.clr())
dir = self.pt2.dif(self.pt1)
if IRand.pct(1) : #branching
r = self.red()+IRand.get(-0.1,0.1)
g = self.green()+IRand.get(-0.1,0.1)
b = self.blue()+IRand.get(-0.1,0.1)
LineAgent(self.pt2, dir.cp().rot(IRand.get(PI/3,2*PI/3))).clr(r,g,b)

if self.frontAngle < 0 :
self.frontAngle+=PI #going in only one direction
r = self.red()+IRand.get(-0.03,0.03)
g = self.green()+IRand.get(-0.03,0.03)
b = self.blue()+IRand.get(-0.03,0.03)
LineAgent(self.pt2, dir.rot(self.frontAngle)).clr(r,g,b)
```

The next code change the previous code to round the turning corner by bending the lines with a few segments. To enable bending through multiple generation of agents, the instance field of bendAngle and bendCount are added to LineAgent class.

```add_library('igeo')

def setup() :
size(480,360,IG.GL)
IRand.init(2)
IG.duration(700)
LineAgent(IG.v(-0.1,0,0),IG.v(1,0,0),0,0).clr(0)
LineAgent(IG.v(500,-0.1,0),IG.v(0,1,0),0,0).clr(0)
LineAgent(IG.v(500+0.1,500,0),IG.v(-1,0,0),0,0).clr(0)
LineAgent(IG.v(0,500+0.1,0),IG.v(0,-1,0),0,0).clr(0)

LineAgent(IG.v(500/2,0,0),IG.v(1,0,0),0,0).clr(0)
LineAgent(IG.v(500,500/2,0),IG.v(0,1,0),0,0).clr(0)
LineAgent(IG.v(500/2,500,0),IG.v(-1,0,0),0,0).clr(0)
LineAgent(IG.v(0,500/2,0),IG.v(0,-1,0),0,0).clr(0)

for i in range(30) :
LineAgent(IRand.pt(100,100,0,400,400,0),IRand.pt(-10,-10,0,10,10,0),0,0).clr(IRand.clr(0.4,0.4,0.4))

class LineAgent(IAgent) :
# static variable constants
length = 2
clearance = 1.99 #less than length
angleThreshold = PI/20
threshold = 10
bendSize = 2

def __init__(self, pt, dir, bend, bcount) :
self.pt1 = pt
self.pt2 = pt.cp(dir.cp().len(LineAgent.length))
self.isColliding = False
self.minDist = 0
self.frontAngle = 0
self.bendAngle = bend
self.bendCount = bcount

def interact(self, agents) :
if self.time() == 0 : #only in the first time
self.minDist = -1 #reset
for agent in agents :
if self.isColliding :
return
if isinstance(agent, LineAgent) and agent is not self :
# checking clearance of end point
dist = agent.pt2.dist(self.pt2)
if dist < LineAgent.clearance :
self.isColliding=True
elif dist < LineAgent.threshold : #not colliding but close
#check if not the parent and in front and closest one
if not self.pt1.eq(agent.pt2) and \
self.pt2.dif(self.pt1).angle(agent.pt2.dif(self.pt1)) < LineAgent.angleThreshold and \
(self.minDist < 0 or dist < self.minDist) :
self.minDist = dist
self.frontAngle = self.pt2.dif(self.pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis)

def update(self) :
if self.time() == 0 :
if self.isColliding :
self.del()
else : #if not colliding
ICurve(self.pt1,self.pt2).clr(self.clr())
dir = self.pt2.dif(self.pt1)
if IRand.pct(1) : #branching
r = self.red()+IRand.get(-0.1,0.1)
g = self.green()+IRand.get(-0.1,0.1)
b = self.blue()+IRand.get(-0.1,0.1)
LineAgent(self.pt2,dir.cp().rot(IRand.get(PI/3,2*PI/3)),0,0).clr(r,g,b)

r = self.red()+IRand.get(-0.03,0.03)
g = self.green()+IRand.get(-0.03,0.03)
b = self.blue()+IRand.get(-0.03,0.03)
#checking bend
if self.bendCount == 0 and self.minDist >=0 and self.frontAngle!=0 :
if self.frontAngle < 0 :
self.frontAngle+=PI #going in only one direction
self.bendCount = LineAgent.bendSize
self.bendAngle = self.frontAngle/self.bendCount

if self.bendCount > 0 : #bending
dir.rot(self.bendAngle)
LineAgent(self.pt2, dir, self.bendAngle, self.bendCount-1).clr(r,g,b)
else : #go straight
LineAgent(self.pt2, dir, 0, 0).clr(r,g,b)
```

(back to the list of tutorials)