pythonboids

Inverse square separation of boids repels boids unevenly


I'm new to programming and I'm trying to make a little boids algorithm in python, so far I've written a method to keep the boids apart from one another using an inverse square function, and it looks like this:

def separation(self, boids):
    repulsion = Vector(0, 0)
    magnitude = 0
    for Boid in boids:
        if Boid != self:
            distance = Boid.position - self.position
            if np.linalg.norm(distance) < 100:
                magnitude = 100/(np.linalg.norm(distance) ** 2)
                direction = math.atan2(distance.y, distance.x)
                repulsion = repulsion - Vector(magnitude * cos(direction), magnitude * sin(direction))

    return repulsion

Since there's no privilege or anything to one boid, any two boids should repel each other with the same amount of force. However, when I ran a test with 2 boids separated by 10 units and no initial velocity, one boid accelerated noticeably faster than the other. I traced the error to the distance variable which the boids use to calculate the strength of the repulsion, and I made both boids print this variable. On the first frame of time, one boid saw the other as 10 units away while the other saw it as 11 units away (actually, it's -11, but since it gets squared the sign doesn't matter). I then printed out their positions and subtracted them to manually calculate their distance values on the first frame of time to see if it had to do with the equation for distance, and it produced 10 and -10, the correct values. I've tried rewriting the distance variable as self.distance and then writing self.distance = Boid.distance to make both boids see each other as the same distance apart, but it made no difference.


Solution

  • You are updating each boid one at a time. Some of the boids see the others move before they have a chance to. This means they never saw the other as close as the other saw them. This is the source of the asymmetry.

    The problem lies in when the result of the separation call is applied to the boid's position.

    You need to update all of the positions simultaneously. That is you need all of the separation calls to be done before you update the positions in reaction to the repulsive force.


    Think of it like this I have two variables

    a = 1
    b = 1
    

    I want to add each of them to the other. If I do:

    a += b
    b += a
    

    I get:

    a == 2
    b == 3
    

    This isn't what I want.

    I want them both to be 2.

    If I do this instead like this:

    (a, b) = (a + b, b + a)
    

    I get what I wanted.

    Now this fancy expression hides a truth. It can be written without as to show this.

    a_ = a + b
    b_ = b + a
    
    a = a_
    b = b_