(train whistle blows) - Hello and welcome to a coding challenge. In this coding challenge, I should probably go eat
lunch but I'm going to just try this coding challenge. I am going to attempt to program this. This is a flocking simulation. What you're seeing right now is an example that I made many years ago of a flocking simulation, a Boids system running in processing. I'm going to try to make
exactly this or close to it in JavaScript with the p5 library starting from no code at all. Obviously besides the dependencies. Okay, so this is the example running. Let me just give you a little
bit of background here. If you want to learn
more about the flocking algorithm as invented by Craig Reynolds, you can go to this link to
read this explanation of it. And find many other links and resources down here, whoa, that's a lot of stuff. Have fun, see you in a few weeks. You can also check out this YouTube video of the original 1986 flocking simulation. Wow, that's amazing. This is so cool, oh my God, I love that. You can also find an explanation of it as part of my Nature of Code book online with some diagrams and
code written in Processing and an exercise that you
can try which I'll refer to back at the end. But I'm going to start
from this, a blank slate. I just have the canvas,
you have some amount of pixels in it. There is nothing happening in the console. I've got this code set up and draw. So I'm going to write this
code without worrying about being perfectly organized
and being able to scale it very easily. I just want to get the
algorithm working but of course, where is the button? (pop music) If you want to see a
refactored example of it, you can probably go and look and actually, at my finished version
in the Nature of Code which I'll also link to. The first thing that
I want to do though is I want to add another JavaScript file called particle.js. Actually, I should rename this to boid.js. What is this term Boid? When Craig Reynolds
invented this algorithm for simulating a flocking
system like a flock of birds or a swarm of bees, I
suppose he didn't want to use the term bird, but
it is kind of like bird, so Boid, it's kind of like droid but Boid. Go read the original paper
about steering behaviors and flocking systems
and you'll find out more about the history about this. But I'm going to make a class called Boid and I am going to use
the p5 vector object. I'm going to make Boid
objects and each time I made a Boid object, I need a constructor and I am going to give
each Boid a position. I'm going to give each Boid a velocity. I'm going to give each
Boid an acceleration. All of those are going to be vectors. I'm doing this is 2D. You should, after you watch
this, if you watch this, you should make your
own 3D version of this. All of the math will
work exactly the same way but you'll just need to rethink
how you're visualizing this. Just to give things a
start, let's just put the position in the middle of the window and let's write a function called show which will just draw them
as a circle right now. Actually, I'll just even use
point, like strokeWeight 16. Stroke 255, point this.position.x. I think Visual Studio
code will do this for me. No, where does it? It fixed that for me once, whatever. This PointerEvent, I'm not a PointerEvent, I'm a point. So now, if I were to
then here make a flock, which is an array, and
I should probably make a flock class, that might be
a thing to refactor later. But I'm just going to
say a flock.push new Boid and then I just want to say
for every Boid in the flock and I'm using of, I'm using a for-of loop but I said in because that's
the way I feel right now. A boid.show and then I need to make sure I am also referencing
boid.js in my index HTML so I have the basic Boid
class, which just creates a position, velocity,
acceleration and draws it as a point and then I have
my sketch where I make one Boid and I draw it in
the center of the screen. There it is, there's my boid. (bell rings) Step one complete. I probably should plan this out but I have one Boid, that's good. Now, let's have that Boid move. Let's actually write in the Boid object a function called update
and this I probably covered ad nauseum in a lot of other videos in my whole Nature of Code series. But the idea here is
that position is updated based on the Boid's
velocity and its velocity is updated based on the
Boid's acceleration. If I now actually create
a p5.vector, a random 2D. I believe this is a
function that will give me a random velocity vector
and I now go in here and say boid.update. Oh I just spelled it wrong. I thought I missed the this dots. Here we go, look, it's moving. It's moving with a random
velocity each time. Okay, now let's just
make a bunch of these. Now I have a system of 100
Boids but it's interesting, look at this, it fans out
in the perfect circle. Do you know why that is? Because I make a Boid and I
give it a random velocity. That random vector is
always of unit length one. The direction is something
different so they're all actually moving with the same speed. If I wanted them to have,
and this is not an important detail to the flocking
stuff, but just to sort of get a sense of how this
vector stuff works, I could say this.velocity.setMag,
which is set the magnitude to a random
value between 0.5 and 1.5. Now, you can see now
they're all moving with a slightly different velocity. This is by the way, a nice
little almost like explosion motif and I could have
them with a real big burst then they slow down. There's all sorts of
physics-y stuff I could do with this but I just want to do flocking. How, how, how do I do flocking? Let's return to this paper here. This is the key behind Craig
Reynolds' flocking algorithm. Three rules. Separation: steer to avoid
crowding local flockmates. Alignment: steer towards
the average heading of local flockmates. Cohesion: steer to move
toward the average position of local flockmates. Something so crucial in
that description is the word local, local. Local, what does that mean? Let's just do, I think
the easiest one to do actually is alignment. Let's say there are five Boids. Actually, let's make a lot more of them. These are all of my Boids. This is the one that I'm
currently operating around. It has a velocity vector like this. Let me just make up, let's just pretend like the ones around it are kind of moving all in the same direction. Because that'll be easier to think about. This is the current one
that I'm looking at. And maybe some of these
others are also moving in other directions also. I could take the approach
to say this particular Boid needs to align itself to everything. But this is not really how complex systems in nature actually work. The emergent phenomena
comes from this Boid having a limited perception of its environment. So maybe, it's actually
only able to see things that are in front of it or behind it or to the right or what's
probably the most simplest thing to implement is within some radius. So I only want to look
at the Boids that are within this radius,
meaning this one, this one, this one, this one, this one. What if I iterate over
all these ones that are within some distance, average
all of their velocities and shift this velocity in the direction of all those velocities. That's what we want to do. I am going to write a function now. I'm going to put it in
the Boid and I'm going to call it align and it's
going to get presumably an array of other Boids. The idea is this function align this Boid with all the other Boids. So what do I need to do? I'm going to make a
variable called average which is going to be a
vector then I am going to iterate over and I could
do all of these with reduce probably in some
higher order array functions. That's a great thing for you to do. (pop music) Refactor it later but
I'm just going to do it this way, I'm actually going
to use i because that's maybe going to help me. I'm not really sure but I'm
going to use i right now. Actually, no, I'll
still use a for-of loop. Let other, I'm going to
call it other of Boids and I'm going to just say
average.add other.velocity. So I'm adding up all of the velocities. This is how you do an average, right? Vectors, again you might
want to go watch my videos about how vectors work,
but a vector, a p5 vector just holds an x and a
y or a x and y and a z. So I want to average the
vector, represented as an arrow with an x component and a y component. If I want to average,
just like I might average an array of numbers, I
add them all up and divide by the total. Then I would say
average.divide by Boids.length. This would be a way of
getting the average velocity of all the Boids but remember I only want the ones within some distance. So actually, let's
implement that right now. Let's have some sort of distance like max. I'll call it perception
equals let's just make it arbitrarily like 100 and
then I'm going to now say if the distance
between this.position.x. You know what? I think there's a p5. This is fine. It's just going to be, it's kind
of long the way I'm writing this but I don't mind. Let's actually make it in a variable. D = the distance between
this Boid's position and the other Boid's position, x and y. If that distance is less than 100, look at that. Look at this fancy thing that it's doing. I have some prettier
stuff that doesn't want to let me write a long line of code. I guess I'll keep that
in there for right now. So I'm calculating the
distance between this Boid's position and the other Boid's position. If that distance is less
than 100, I'm adding it up and I should also
probably have a total number, total = zero. Add it up. I also should probably
ignore myself and if other does not equal this, right? So I basically, I might
as well put that first. As long as the other thing is not me and the distance is
less than 100, add it up and then divide by the total. But obviously, I only want
to divide by the total if total is greater than zero. All right, so this is
kind of a little bit of a long-winded algorithm now. And perception. If d is less than perception. So I'm starting with a
perception, a radius of 100. Maybe that would be better if I called it perceptionRadius, that's
really what it is. Starting with a perception radius of 100, an empty vector, adding up the velocities of any vector, any Boid that's near me, dividing by the total and
as long as I've found one, dividing by that total. Now here's the thing. I could just say, for
example I could do something weird, this will be really weird. I could just say
this.velocity equals average. What if I did that? Then I said Boid, like here
I said boid.align flock. I don't know what's going to happen here, but let's refresh this. They just basically don't go anywhere. Because they're all instantly equal to each other's velocity. That's not really a good way. What I need to do now is
if I don't want to actually assign its velocity to
that average directly. I want to steer towards it. This is where Craig Reynolds'
steering formula comes in. A steering force equals
some desired velocity minus the current actual velocity. It's kind of like the error. If I am moving this way. Sorry, if I'm moving this way
but I want to move this way, the way I want to move
minus the way I am moving is the steering force meaning
push me in that direction. Apply a force. Maybe I'm going to turn
the steering wheel. You can think of these as bees or cars or birds or whatever. This formula desired minus
velocity will give me the steering force and what
is the desired velocity here? The desired velocity is
actually that average. I'm actually going to
rename this to desired and I'm going to add it up and divide by total and
then I'm going to say this.velocity equals, no,
I'm not going to say that. I'm going to say steering equals. I should actually call this
steering because I could do all of this. I don't need to save
anything as I'm going. I'm going to call that the steering force. I'm going to add all the velocities. I'm going to divide by the total and then, I'm going to say steering
force is the desired subtract this.velocity. And I'm going to say return steering. I want this function to basically, I want this function to return that force. So really what I should
be doing is I should have, I'm going to write a
function called flock. That's maybe more like, yeah. Flock with some number of
Boids and then I'm going to say alignment equals align with those Boids. So I'm going to get
this force and I suppose steering is here, so you know what? I should always return steering. I want to always return a
vector but if it didn't find anything, I'll just
return the zero vector. Then, what I'm going to
do is I'm going to say acceleration add alignment. The idea, the way that
a physics engine works, and this has to do with, this is a very simple
crude physics engine. Force equals mass times acceleration. This is Newton's Law of Motion. Or acceleration equals
force divided by mass or in a world where the
mass of everything is one, acceleration equals force. So if I want to apply
a force to this object, I just need to set the
acceleration to that force. Actually, that's what I'm going
to do just starting out here. I'm going to say acceleration
equals alignment. So now, if I go back to sketch and I say Boid.flock Boids, we should
now have all of Boids doing that with just implemented
with that alignment rule. Let's see. Boids is not defined. Sketch.js line 14. Oh, it's called flock. Oh, this is my variable
naming is terrible. But I'm going to leave
it that way right way. Align is not defined. Boid.js align Boids. Oh, this .align, this .align Boids. Acceleration is not
defined, oh my goodness. (pop music) Let me actually not have them all start in the same place because
that is just to see this effect happen, let
me actually start them in random places on the screen. Let me change that. Let me make the perception
radius a little bit smaller and I don't know why this
matters, but let me draw them like a little bit smaller. Let me actually have them start out going quite a bit faster. You can see as they get near each other, they start to get each other's velocity. They start to average each
velocity with their neighbor's. You start to see these
clumps moving together. So much more to do on this. Oh my goodness, but first a couple things. One is I have now basically
allowed these Boids to steer with infinite power in a way. We should probably have a variable. I'm going to call it maxForce. I'm going to set it
equal to one for a second and then what I'm going to do is here, I'm going to say a steering
limit this.maxForce. What this does is it limits the magnitude, the length of that vector
to some maximum force so this should be exactly the same. We're not seeing anything different but if I were to make maximum force
0.01, like really small right now, you're going to
see they're not actually changing their velocity. They are, but very, very slowly. The other thing we're
really going to need to do is I just have to give up and do this. I'm going to add something
for the edges here. I'm going to say if
this.position.x is greater than the width, this.position.x
should equal zero. Else, if this.position.x
is less than zero, this.position.x equals width. Then I'm just going to do that for the y, with, with, x, x, y, y, y, x, x, x, x, with, with. All right, and now here,
let's do boid.edges. We should see them
reappear wrapping around and now, let's go back to the Boid and let's make this 0.2 is
kind of reasonable-ish. Interestingly enough, they're kind of like it slows them down which is interesting. But I'm only doing alignment right now, but you can see how this works. You can see how they're
all starting to align with each other. I could keep them going
at some minimum velocity which might make sense. You can see how these
are going back and forth. But you get the idea. Why are they all slowing down? Is that just a coincidence? One thing I could do is this is actually going to be worth it because I should also
give them a maximum speed. This is going to be a
parameter of a variable that's going to allow
me to control the system pretty well and I could
consider their desired velocity in the alignment
to not actually be the actual average velocity
but just the average direction because I could
then say steering.setMag to this.maximum speed. So basically I'm saying I
always want to go in the direction of my neighbor
but at maximum speed. I don't know that that's
really an important detail, but if I add that in here,
we might get the effect that I was hoping to get. Now, this is what I was expecting to see. As they get near each other, they're all starting to align together. Let me refresh that one more time. You can see that they're
clumping and as they group, they all start to align. This is the alignment rule. This is a very simple rule. It's predictable, it's
obvious, this is alignment. Now, all we need to do is
add cohesion and separation. Separation is going to
be the hardest one to do. So let's do cohesion next. Same thing, we're going
to look at our neighbors. We should go back to what
the rule was actually defined as, let's go back to
Craig Reynolds' original page. Cohesion: steer to move
toward the average position of local flockmates. This is actually not going to be that hard because we've already
calculated the average velocity of local flockmates. Now, let's just duplicate that code. I know, I know we could refactor it. I won't play the song. Because we could probably do
this all in one fell swoop. There's so many possibilities
but now I just want to get the average location
of my local flockmates and steer towards that. So, the way I'm going to do
that is I'm just going to go nuts and copy paste this whole thing and call it cohesion and I'm going to keep the perception radius, I'm
going to keep this idea of steering, keep this idea of total. Go through all the
Boids, check the distance between myself and the other ones, as long as I'm not myself and
within the perception radius, what am I doing? Steering not the others' velocity
but the others' position. This should clearly be
refactored into its own function but whatever. Then as long as I have
at least one, I'm going to divide by the total. Now I don't want to set the
magnitude to the maximum speed so what I have now in steering
is the average location. So if this is the average location, let's say it's over here, then what I need is a vector that, that's clearly not the average location. But let's just say it was. The average location is kind of like where this Boid is. But I want to steer in that direction so to get a vector in that direction, I take the average location
minus the current position of me which is basically saying now what I want to do
is say steering subtract this.position so subtract my position now, I've got a vector that's pointing from me to the average location,
remember which is in the steering variable
then let's say I want to go at maximum speed. Then, this is now my desired velocity. I'm going to subtract
out my current velocity, limit it to max force and there we go. That's cohesion. (bell rings) Now, in flock, let cohesion
equal this.cohesion Boids and then oh, we have a big problem. This.acceleration equals alignment. This.acceleration equals cohesion. So how can I set the acceleration
to two different forces? But before I even answer
that, there's an easy answer to that question. Let's just comment out
alignment and let's watch cohesion happen. We can see they start to group together. This is cohesion happening. And that neighbor radius, by the way, is a super important value. Right? That neighbor radius,
if I were to change that and I have a different,
that perception radius, if I were to change that to 10,000 they're all going to come together as one. Because they all see everybody. If I were to change that to 10, there's really little pairs. You can see they kind
of get in groups of two. The force though isn't so strong. So if I were to change
the maximum force to one, now you see them, almost like these little electron thingies that
start, magnetic things that start to spin around
each other and then fan off. There's so many possibilities here. I could make the maximum speed two. I'm not actually limiting it, you can see. There you go, that's
what I was looking for. They get into these little groups but let me go back and
say maximum speed is four. Maximum force is 22 and I realize this maximum speed is not
actually a maximum speed because in update if I
really wanted it to be a maximum speed, I would want to say this.velocity limit this.maxSpeed. Let's actually add that
in and see what we get. Oh, and let's put the
perception radius back at 100 and there we go. Now we have cohesion. We have cohesion, what
happens if we have cohesion and alignment? How do we get cohesion and alignment? Okay, this is fun. This is really working. This is going to be a really long video. All right, cohesion and alignment. The problem rests here. This is actually a really
easy thing to solve because this is called force, the answer here is force accumulation. In physics, if two forces
are acting on an object, the resulting acceleration
is the sum of those forces. So all I need to do is say
acceleration.add alignment and acceleration.add cohesion because why is this not falling? Because the force of
gravity is pointed this way and the other force of
my hand holding it up is pointed in the other direction
with an equal magnitude. Therefore, it is at rest. Those two forces added together
have a net acceleration of zero. But obviously, all you
do is add them together but there is this, if I
actually do this right now, we're going to see some
really crazy behavior. Looks like it's kind of
working but it really isn't because what it should also
have is this acceleration shouldn't accumulate over time. Every moment in time, I
start with a net acceleration of zero and add all the forces together. So what I should do right
before I flock is say this.acceleration set zero, zero. This is setting its
values to zero and zero. Another way I could do
that is just multiplying it by zero, because
multiplying a vector by zero takes everything out. It could make a new vector, whatever. But this will work nicely and technically, this also might make more sense here because it's kind of like
after I finish updating the velocity then I can
reset the acceleration in case there's other things at play. Now we can see both cohesion and alignment are at play, look at this. Eventually, they're just all
going to become this one clump. Come on, you can catch
up, you got it, go go! Go, catch up! Yeah! One clump. One thing I would like to do. I really want to attach
sliders to those parameters but I got to resist because
we got to have separation. We've got alignment. (bell rings)
And cohesion. (bell rings) Separation, this one is a little bit hard. Steer to avoid crowding local flockmates. Here's the me. That's me, here's my local flockmate. If this local flockmate
is too close, it's within some sort of distance
threshold, I want to steer to avoid that. What would be my desired velocity? My desired velocity would be to move in the opposite direction. So the idea is my desired
velocity is in the observable. What if there is one here? My desired velocity would
also be opposite direction so then my next desired velocity would be the average of these two. It even would make sense
for the magnitude of that desired velocity to be
proportional to the distance. So if this one is here,
I maybe want to avoid it but I only really need to
think about avoiding it a little bit. If this one's really close,
I need to avoid it a lot. So the magnitude of the vector
is inversely proportional to the distance of the local flockmate. This might end up with kind
of like a desired velocity of something like this if I'm
averaging those two together. So let's see if we can implement that. Not going to be super
easy but let's do it. The nice thing is I'm going
to start with cohesion because we've kind of, cohesion is close to separation. Again, boy. (pop music) ♪ I will refactor this later ♪ ♪ You know I will refactor this later ♪ ♪ I will refactor this later ♪ - I should show you that I'm wearing my I will factor this later
t-shirt, by the way. Okay, so separation. I'm going to leave this at 100. Steering, I think I might need to use, I like the idea of just having one vector that ends up as, it's
sort of the average here then it's the difference
but I think I need to think, one step at a time. I'm creating this vector. This is the same. If it's not me and it's
within the perception radius, what do I need? I need a vector, I'm going
to call it difference which is the other's. I want a vector, sorry, that points from the other to me. Because I want that to go
to the opposite direction. So it's my position minus the other. I want a vector, the new vector which is the subtraction between my position and the other's position. This is a way of calling
the subtract function which doesn't operate on a vector but subtracts two vectors and gives us a new vector which is difference. Then, I want the
difference to be inversely proportional to the distance so I'm going to multiply
it or set its magnitude something like that. Or divide by distance. I could just say divide
by distance, right? It's inversely proportional,
the farther away it is, the lower the magnitude. The closer, the higher the magnitude. The distance, I guess
technically that distance could be zero and that
would be problematic but with floating point math rule, it's never going to get a value of zero. Then I want to add. Then that is the thing I add up. That's the thing I want to average. All those vectors that are pointed away from the things near me,
I think we're actually in pretty good shape here. This isn't as hard as I thought. This was the tricky part here. Get a vector that's pointing away from the local flockmate,
make it inversely proportional to the distance, add it
up, then this is the same. Because, oh no. I don't need to subtract position. I kind of don't know if I want to. Let's leave this whole always
go at maximum speed thing and then the rest is the same. Once I have that, just average it, set it to max speed,
subtract out the velocity and limit it to maximum
force and that's the end. Okay, just to be sure this is right, let's go into Boid. Let's go to flock. Let's add let separation
equal this.separation and then let's add in separation. I want to not bother adding
cohesion and alignment. Are these separating? Yeah, it looks like they are. It looks like when they
get close to each other, they're kind of moving
away from each other. Weird, weird behavior. This doesn't look like
exactly what I expected. Let's give the maximum
force, like really let them be super strong about separating. There we go. So that's more like what I expected. That's way too strong. Yeah. Why do they feel like they're,
something is weird here. Let's give it a much
smaller perception radius. Oh yeah, this is what I expected to see and I think it wasn't
really a bug in my code. It's sort of a bug in my conceptual sense of how this works. This is pretty good, this
is what I want to see. This is separation only. Now, we should be able to see and let's I'm going to do this really quickly. I'm going to not be thoughtful
at all about the interface but just so I can debug this effectively, I am going to go into sketch.js. I am going to say let
alignSlider, cohesionSlider and separationSlider. AlignSlider equals createSlider. I'm going to give it a
range between one and five. Actually, let's give it
a range between zero, sorry, zero and five, with
a starting value of one and increment value of 0.1. I want to do this for
all of these sliders. Then in Boid, I want to
say where I call in flock, I want to just scale them. I'm going to say separation.multiply
separationSlider.value. I'm going to do this also for
cohesion and for alignment. Now, by the way. (bell rings) (train whistle blows) Flocking. This is now the flocking simulation. I didn't draw them as triangles. They're not rotating, but this
is the flocking simulation. But just for the sake of
argument, this should be what order did I make them in? They're not labeled. So let's just turn all
the way up to separation. Oh boy. Let's turn off. This is only separation. Now, turn that off. Let's turn on only alignment. Whoa, that's oh they're just the alignment is so strong. When the alignment is
so strong, it makes them go in circles around each other. I have to talk about why. Why did that happen that they
were twirling in circles? Well, look at this. I'm updating them one at a time. The next one is flocking
with the previous ones updated value, right? Instead of taking a snapshot
of all their current velocities and then each one updates based on that snapshot, I start
with a set of velocities. I update the first one based
on the set of velocities and now its new velocity is there. So when the next one, it's
actually updating itself based on the previous one that I updated which is not how the world works. It's not how time works. That's causing them to
ripple into each other based on the order and go in circles. That is something that you
might want to consider fixing. I'm not going to fix that
but now I can really play with these, I can get
different sort of qualities of flocking based on how
strong I make these rules. So no alignment, a lot of cohesion. No separation, add a lot of separation. Less cohesion. Separation, I shouldn't make too strong but it's important and then
let's add some alignment back in and there we go. Flocking. (bell rings) Okay, this coding challenge is complete. It is done. I will be uploading the
code, if you want you can look at the video
description to find the code as a snapshot, that's exactly what it is. Let's go make a list. Let's make a list on the board of things you might try to do. Number one, snapshot of all velocities. That's going to be my code for that. Number two, an optimization you can do is called spatial subdivision
or a quadtree optimization. One of the things that
makes this really slow. And I'll just show you here for a second, is if I were to try to do
this with a 1,000 Boids. Look how slow this is. But you have to realize,
it's not a big deal for p5 to draw 1,000 things moving. Why it's fine drawing 1,000 things moving but as soon as I put
the flock function in, it's super slow. The reason that is is
because it's got to do and every Boid, check
every Boid against every other Boid check. So there's a way of subdividing the space, spatial subdivision into bins or buckets and have the Boids only
check ones that are near each other in the same buckets. That's called, I probably
have a lot of distance calculation, I could
reduce the number of times I call it. But for this, quadtree or just
simple spatial subdivision. These are things you could try. These are just code refactoring, really. Actually, the other thing you could try is really build a more
sophisticated interface and there's lots of
other parameters that you could control and try. There's the perception
radius, there's maximum force, there's maximum speed. Each rule could have a
different perception radius. So many possibilities. This is the stuff that
you could do to really control and tweak all the
values and play with it. Another thing you could do is just design, visual design, like are they triangles, circles, are they flapping butterflies. How do you, design of the Boids? Make your own beautiful
visualization of how you design and draw them. Tadpole-like, there's so many ideas there. Another thing you could try to do is 3D. Can you extrapolate this into 3D? If you see the word
quaternian anywhere you start to research, you might want to turn back. But you could extrapolate this into 3D. Another suggestion which
came from the chat, thank you, is Boids with
different parameters. There's no reason why
every Boid has to have the same max speed or maximum force. Or same perception radius. You could implement this
thing called the view rule. There's two things about this one. Each Boid can see everything around it. But what if the Boid
itself could actually only see if it's moving in this direction, things that are within its
particular view in front of it. Maybe it doesn't deal with
anything that's behind it. It doesn't try to do cohesion
or alignment or separation. Then the view rule which
is posited by Flake in the book A Computational
Beauty of Nature, I'll link to that book also. It's a wonderful book with tons, probably like 90% of
the things on my channel are all from the Computational
Beauty of Nature book. But Flake posits this additional rule that you always want to
keep your view empty. So if there is a Boid in front of you, you want to steer that way
to keep your view empty, clear and this might result, the theory is that this will result
in something that looks more like that pattern that you'll see of actual birds flocking where they kind of appear almost in this triangular pattern. What's interesting about
this, you see a pattern like this, you think, "Ah ha." This is a top-down behavior. There is a leader, this
bird, who is saying to all the other birds, "Fan
out from me and follow me." But yet, this type of intelligent behavior emergent phenomena can
come actually only from simple, local rules of interaction. This by the way, this
is what this whole video was about, if you didn't
gather that already. Other ideas might be adding obstacles, other forces, there's thinking about these living in a world where
they're interacting with other things,
maybe there's a predator that comes in and is chasing them. All sorts of unique and
interesting possibilities. So thank you everyone for
watching this coding challenge. Put your other ideas for
things people could do in the comments and then
if you make your own version of this flocking
code, please go to the codingtrain.com challenge
page and submit a link to what you made. I will also make a
version of this that runs in the web editor and link
to all those resources like the Craig Reynolds original paper about flocking, the video
on YouTube and the chapter in my Nature of Code book, okay? Thanks for watching this coding challenge on flocking and I'll see you soon. (train whistle blows) (upbeat music)