Boids- investigating emergent group behaviour

Flocking

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