Imagine a 2D space occupied by moving birds. We refer to them as 'boids'- a pun on bird/droid and on New Yorker's pronunciation quirks! Each is only aware of close neighbours, and responds via simple reflexes and learned responses. It is attracted to close neighbours, but at close range avoids collisions and obstacles or walls. And out of this in real life something magic emerges.
As a programming task we have to keep track of each boid's x, y positions and velocities. We calculate the acceleration from simple rules, and update velocities and position. But potentially there is a LOT of calculating to do. In a real flock near here they regularly see up to estimated 1,000,000 individuals. Luckily no one bird interacts with all those- which would be intractable- but you have to think of ways a bird will interact only directly with immediate neighbours- even though they may change!
Below is my current version. You might visit the thread at Liberty BASIC for further ideas.
' ********************************************
' ** **
' ** tenochBoids4.bas 16/03/21 **
' ** **
' ** create a field of flocking boids **
' ** **
' ********************************************
nomainwin
WindowWidth =1120
WindowHeight = 630
' Each boid represented in a csv string of x, y, vx, vy
' x/y onscreen if in o...600. Velocity of 1 represents one pixel per cycle.
global xCG, yCG ' coordinates of centre of gravity of flock
N = 50 ' number of boids
dim boid$( N)
open "Tenochtitlanuk Boids" for graphics_nsb as #wg
#wg "trapclose quit"
#wg "down ; size 10 ; fill 80 80 255"
loadbmp "scr", "sky.bmp"
gosub [createBoidField]
[next] ' cycle the updates
gosub [drawBoidField]
gosub [updateBoidField]
scan
goto [next]
' __________________________________________________________
[createBoidField]
for i =0 to N -1
x = 50 +1000 *rnd( 1)
y = 50 + 500 *rnd( 1)
vx = 2 + 8 *rnd( 1) -4
vy = 2 + 8 *rnd( 1) -4
boid$( i) =str$( x) +"," +str$( y) +"," +str$( vx) +"," +str$( vy)
next i
return
[drawBoidField] ' draw current flock.
#wg "cls ; drawbmp scr 1 1"
#wg "color red ; size 5 ; up ; goto 2 2 ; down ; box 1105 597 ; up"
for i =0 to N -1
call drawBoid boid$( i)
scan
next i
return
[updateBoidField] ' calculate new boid flock.
gosub [findCofG] ' find xCG, yCG of centre of mass
for K =0 to N -1
x =val( word$( boid$( K), 1,","))
y =val( word$( boid$( K), 2,","))
R$ =rule1$( K)
vx =val( word$( boid$( K), 3, ",")) -( x -xCG) /10000'-1e-1 *val( word$( R$, 1, ",")) *sin( val( word$( R$, 2, ","))) ' *angle fn.)
vy =val( word$( boid$( K), 4, ",")) -( y -yCG) / 6000'-1e-1 *val( word$( R$, 1, ",")) *cos( val( word$( R$, 2, ",")))
vx =0.9999 *vx
vy =0.9999 *vy
x =x +vx
y =y +vy
if x <=10 or x >=1090 then vx =0 -0.9 *vx ' implements ricochets off sides.
if y <=15 or y >= 585 then vy =0 -0.9 *vy ' could instead wrap top/bottom and left/right- toroid world.
boid$( K) =str$( x) +"," +str$( y) +"," +str$( vx) +"," +str$( vy)
scan
next K
return
[findCofG] ' place CofG position in global xCG, yCG.
xCG =0: yCG =0
for f =0 to N -1
xCG =xCG +val( word$( boid$( f), 1, ","))
yCG =yCG +val( word$( boid$( f), 2, ","))
next f
xCG =( xCG /N +500) /2
yCG =( yCG /N +300) /2
return
function rule1$( K) ' accelerate towards centre of mass of flock.
dx =val( word$( boid$( K), 1, ",")) -xCG
dy =val( word$( boid$( K), 2, ",")) -yCG
R =( dx^2 +dy^2)^0.5
angle =Atan2( dy, dx)
rule$ =str$( R) +"," +str$( angle) ' return a string with magnitude and angle of accn towards CofG
end function
sub drawBoid i$ ' draw an individual boid
#wg "color darkblue" ' draw boid
#wg "size 10 ; down"
x =val( word$( i$, 1, ","))
y =val( word$( i$, 2, ","))
#wg "set "; int( x); " "; int( y)
vx =val( word$( i$, 3, ","))
vy =val( word$( i$, 4, ","))
magn =( vx^2 +vy^2)^0.5
dirn =Atan2( vy, vx)
nx =x +vx
ny =y +vy
#wg "color black" ' draw tail, length proportional to velocity and along track
#wg "size 1"
#wg "goto "; x -4 *vx; " "; y -4 *vy
end sub
Function Atan2( y, x)
If x = 0 Then
If y < 0 Then
Atan2 = -1.5707963267948967
Else
Atan2 = 1.5707963267948967
End If
Else
chk = atn(y/x)
If x < 0 Then
If y < 0 Then
chk = chk - 3.1415926535897932
Else
chk = chk + 3.1415926535897932
End If
End If
Atan2 = chk
End If
'thanks Andy Amaya ( one of several versions I use)
End Function
sub quit h$
close #h$
end
end sub