This web page is a visual deconstruction of Craig Reynold's Boids Algorithm.
Or so, I would have you believe.
I am no expert...
Individual Flocks The above layers in BW
If you are only interested in the destination,
then look at the code.
However, if you're interested in the journey,
try to follow along.
I bore easily,
so I tend to move fast,
perhaps, too fast.
Prototyping: Getting Something on the Screen
Yeah, so maybe I should talk briefly about Boids. I believe (as in, I can't be bothered to fact check any of this) that Craig Reynolds first came up with the idea back in the early '80s. And the concept was along the lines of self-ordering motion by following three simple rules:
Rule #1: Boids Seek Each Other
Boids tend to move toward the center of the flock
Rule #2: Boids Seek Same Heading
Boids tend to match the velocity of their neighbors
Rule #3: Boids Avoid Collision
Nearby Boids tend to move apart
Note: These are steps on the way to failure:
the failure before success.
Boids wrap around screen.
Movement is mostly chaotic (not-debuggable).
Starting with the Known
It's hard to debug,
that which is unknown.
From there, pretty much anything is possible:
So, let's start with the known.
And go from there.
Rule #1 (only)
Boids Seek Their Own
Rule #3 (only)
There is a limit to Rule #1
Should have used shorter GIF's for this,
as the gifs are black for far too long.
Adding goals (set location attractors)
leads to pretty pictures
More Pretty Pictures Using different goals.
Mainly because I can.
And I think they look cool.
Tying It All Together
I call this set of pictures:
Some nice flocking behaviour in there, I'd say.
Being Brief in my Debrief
For me, part of the purpose of this web page is as a code walk-through (slash debriefing). Sure, no code. But that's not always the important part. See these test patterns? There's some chance of determining whether the code is working correctly from these (along with the less visually appealing test patterns of center, vertical line, and horizontal line, which were often quite boring, so not shown). Anyway, long story short, good test patterns, often leads to ease of debugging. Sure, I'm not showing you that vertical line that just bounces up and down. But I can assure you, when it didn't bounce up and down, I knew something was wrong.
We don't need no stinking tests!
Um, yeah. If I knew how to write a meaningful test for this sort of program, I probably would. The difficulty of doing such a thing (or down right impossibility) actually makes me wonder about the general validity of code base test coverage.
And this is where having some code to talk above might be handy, so here's the code for Rule #1. And no, in the absence of looking at the rest of the
boids raw code it probably won't make a lot of sense.
'''distance weighted increment of self.delta,
based on POSITION of other boids in flock
(in essence, each boid tries to move toward the center of nearby boids)
Rule #1 Implementation
vel_flock_pos = + vector / distance
for b1 in self.boids:
ave = np.array([0.0,0.0])
for b2 in (b2 for b2 in self.boids if b2 != b1):
ave -= (b1.pos - b2.pos) / b1.distance(b2)
And seriously, the only thing I want you to take from the above, is the following formula:
So, is that a good formula? A bad formula? Does it reflect the rule? Is it the best formula for the rule? You know what? I don't know. And no unit test it going to tell either one of us the answer to any of those questions.
The only difference here is that the influence of each boid decreases by the square of the distance for Rule #2 but only by the distance for Rule #1. Of course, by the time we get to Rule #3, the implementation is lots of fun because I decided to go with the negative of the cube of the distance.
Now, there's nothing wrong with these equations. They work as far as my purposes go (i.e. making a Boid prototype). But that doesn't mean the equations are Right in any meaningful sense. All a test would do is confirm that my code was right or wrong consistently not that it was actually right or wrong.
Anyway, end of rant.
Overwhelmingly, my code tends to revolve around vague feelz and aesthetic concerns, so that's probably why I've only written a half dozen asserts or tests in all my live long years.
Now, if only I could remember what I wanted to discuss next (before getting back to the pretty pictures... got to end with the pretty pictures).
Object Oriented Programming
Or as I like to call it,
Programming with Class
I'm not really big on classes. In truth, I often feel like they would be overkill. In the
(and dammit, I'm going to link to that code so many times, you're likely going to open the file by accident, clicking on it with a wayward thumb if nothing else), I use three classes:
which manages the image creation logic
which manages the groups of boids
the individual boids
which eventually become the screen pixels
And that's pretty boring, so maybe you're wondering why I even mention it. Or maybe you're thinking, if I used three class structures in this project, how is it even conceivable that my average project doesn't contain a single solitary class... a statement, which is, of course, a lie (so, good job on calling me out on it).
In this program, I make extensive use of these additional classes:
How I love thee, let me count the ways.
Obviously, they follow from the counting reference above.
For those occassions when more nuanced measurements are required.
But those aren't the fun classes that I use in
boids.py . Want to know what the fun classes are? Huh? Do you? Do you? Do you?
Yeah, that's right. A numpy array.
Actually, I can't be bothered to look up the class name.
import moviepy.editor as mpy
So, probably the class is ImageSequenceClip
And right there is the true power of Python and why I've created so few classes over the years: the readily available classes often make creating one's own classes a silly-silly mistake and/or a common beginner's error.
Or not. Really, what do I know?
Um, yeah. So, what do you say? Is that enough text to call this a serious page, yet?
Eh, guess it doesn't matter. That's pretty much what I had to say.
When you hit it,
the only place left to go
Boids can have a starting velocity, now (downward in these).
And there is a limit to how far they fall (i.e. there is a floor).
The spice of life.
And before you know it,
Bye Bye Boidie
Short and sweet?
Might as well, while I'm able...
One of the later gifs I wanted to load was too big or something. So, I wonder if I've loaded too many gifs onto this one page. A 10,000 pixel image graphic will kill most modern (2015) browsers. (Been there, done that.) So, who knows what 35 gifs will do.
And since I'm running out of speed (motivation) to write this page (and so, it looks like most of these entries will be short), let's just say that although (here and there) I've billed myself as an image manipulation guy, for the most, better graphics than mere pixels don't matter that much to me (though I am hoping these images look better on a phone than my desktop, but they probably won't).
One down, who knows how many to go?
Hawks & Doves
Striving for better,
or winding up worse.
So, where do we go from here?
The next step for this project is to add Wind (randomly shifting horizontal background motion) along with Hawks & Doves (one flock that chases another, which is in turn is trying to move away, the later kept on the screen by attraction to a pile of seed -- a goal -- focused on the ground). Alas, at this point, that would be feature creep (as would making the colors better or adding a small image to replace the boids). So, best to sweep it under the table for now and move on.
Don't talk to me about class.
Here's a real short comment. As the complexity of a program increases, so must the number of classes and the use other organizing techniques. Want to know where I really am in programming? I'm just at the place where adding a few classes to my scripts actually makes sense.
Speaking of which (the future, that is)...
Cellular Automata Graph Theory
You heard it here first!!!
Heck, I don't even know what this means, so maybe I'm running on empty or maybe I'm getting the last easy one out of the way before I hit my main rant. All I know is that, in my heart, I believe there's just got to be some sort of interesting cross over between the two. Boids is pretty much exclusively Cellular Automata. I wonder how this program would look and/or how the outcome would differ if it was powered by Graph Theory. Just a thought.
And now, for our Featured Rant of the Day.
What's it for?
What are you going to do with it?
Actually, I'm going to start with a slightly different question: What can one do with Python? What does it do?
Whenever I talk to non-programmers (which is pretty much everyone I know, considering I work in a one person shop, etc., etc.) in expressing an interest in what I do (my hobby), they want to know what Python is, what can it do?
I've come to realize it's the wrong question, because, as we all know, most every language is Turing Complete, so pretty much every language does everything. But this doesn't suffice. What non-programmers really want to know is why a person would want to learn Python and/or it's real world practical application.
Python is a fast (to write) and easy (to learn) scripting language that is amazingly useful in the production of throw away code and prototyping. But that kind of sentence doesn't have much meaning to a non-programmer. So, perhaps, Python is one of the biggest, widest, easiest to navigate portals into the world of programming and this in turn makes it one of the most cost effective languages for a company to use. So, just in case you were wondering why you should turn your company into a Python shop and then offer me a fantastically high paying job, well, now you know.
I mean, I could say something about how it is fun, how one is either expanding or shrinking, living or dying, how I'd rather make games (not that I do) than play them (once again, not that I do), but all of that is lacking, doesn't really suffice, because: OK, you've got to do something, and programming, fine, it's what you've settle on, Mr Brett, but what are you planning on doing with it, someday, down the road?
And I can't answer this question, because I don't know; but then, I do. You see, I don't know the project that I'm going to do next, what I'm going to start in the fall when I get back from my Wild VacationTM. It could be another Cellular Automata thing, something in Graph Theory, pick this Boid thing up where I left off, a combination of all three, or something entirely new. I don't really know.
Want to know what I do know?
The next project is going to be more involved that this one, it's going to be more high-level (whatever that means), it's almost definitely going to have more classes, more intricacies, more mental gymnastics involved at its root. And maybe, just maybe, that next project will be worthy of being called a Life's Work... or the one after it... or the one after that... or the one I don't get around to coding for another dozen years (if I'm actually still interested in coding in a dozen years).
And that Life's Work, that's the Why? I don't have a clue what it will be, but when I code it (or only a few short decades after I do), I'll know what it is. And that's what I'm doing, why I'm doing this, what it's all for.
And as for Python? Well, right now, it's the best horse I can find that doesn't try to buck me off the moment I get comfortable in the saddle, if you know what I mean.
So, um, there's that: Life's Work, and all.
And then, there's the money.
Oh, and I do like a pretty picture.
I like it when they move.
Most of all, I like it when they come to life...