Skip to main content

Image of Boid Movement. Energy and Selection

·969 words·5 mins

Info

This post was imported from a personal note. It may contain inside jokes, streams of consciousness, errors, and other nonsense.

I’ve got boids tracking their energy. I temporarily added removal of boids when their energy drops to zero but it introduces a problem with the selection phase. Currently I select the top four and repopulate. But if fewer than four boids remain at the end of simulation it’s gonna bork. And what if fewer than four equals zero?

I could… run the sim until four boids remain, 80% of the food is eaten, or we reach 5,000 steps. Four boids remain: I’ll need a listener to find out when boids get removed. That’ll end the simulation and still give me four boids to work with for populating the next generation.

Extinction might be useful… some of these boids probably don’t deserve to survive, lol. I just saw boids that basically go in circles near the origin and avoid any food they manage to encounter and that’s after 30 generations of evolution. Sometimes it’s just bad.

Showing how bad it is requires a long time seeing them not getting any food which means a bigger file size. But I just had a thought. Maybe I can show all positions visited by boids over time in a single image. image Heck ya. Python + Imagemagick ftw. It’s ugly but it definitely tells the story. Nine out of ten boids circle the origin. One boid split off and didn’t actually pick up any of the food in its travels.

That’s only 329 steps, though, so things might’ve worked out better for orange boid down the road, who knows.

Nice Moire patterns on the left side, too. (-:

Might be useful to generate these images in the actual application, that way I don’t need to record screenshots and combine them, I can just add to an image in memory as the simulation progresses and optionally save it to disk at some point.

Is my “Languages” section for the BraitenBoids repo on Github gonna change now? image

Oh wow, that was fast. Less than a minute since pushing the commit? image

What about for boids that do a better job of getting food? image image Pretty cool seeing these side-by-side.

Some of these are still steering away from food.

  • An interesting metric might be how often a boid that detects food ends up getting it.

image image

Fun. If I develop this further, some indication of direction of travel would make interpreting this a little easier. Maybe fade the previous frame to white a little bit before writing the new frame over it.

Feels nostalgic after all that time spent playing with Processing back in the day.

Back to Energy and Selection
#

I want a SimRunner class to separate two concerns:

  1. Simulation - running the simulation step-by-step and maintaining the list of boids, food, etc.
  2. SimRunner - adding boids, positioning food sources, and running multiple steps (fast forward).

The SimRunner will probably eventually coordinate multi-generation strategies like teaching boids to find food for the first ten generations then teaching them to avoid poison for the next ten generations. Or maybe that’ll be another class that orchestrates SimRunners, we’ll see.

For now, what should the interface look like?

For MainVisualize:

Simulation sim;

// <set up SimRenderer>

SimRunner runner(sim);

runner.init(); // Adds boids and food sources, setting their positions, etc.

while (window.isOpen()) { // main loop
  sim.setPlayerDirection(getInputPlayerDirection())
  sim.step(elapsedSeconds);
}

What about reporting? Should SimRunner provide info like how much food was consumed or how many steps were executed?

For MainEvaluate:

Simulation sim;


// <set up SimRenderer>


SimRunner runner(sim);


<for 30 generations...> {
  runner.init();
  runner.run();
}

Right now the Evolution class has the selection and mutation algorithm. That might need to move to SimRunner so it has all the information for running a generation. Yeah, then I can have different SimRunner classes which train the boids different behaviours.

I do want to have the same code controlling both MainVisualize and MainEvaluate.

MainVisualize doesn’t need any of the generation end conditions or selection and mutation. Oh wait, that’s not true. Pressing the ’m’ key mutates and pressing the ‘f’ key fast-forwards to the end condition and then mutates.

void MainVisualize::fastForward() {
  runner.run();
  runner.init();
}
void MainVisualize::mutate() {
  runner.endGeneration(); // ?
}

Hmm. That’s trickier.

Should SimRunner use Simulation’s API to add remove stuff or should it provide an interface Simulation can use… hooks to initialize or handle events or something?

Questions from Demoing for Jason
#

  • Why are the boids all starting at roughly the same speed?
  • Why does the simulation take so long?
  • Should I set up genes as an additional layer on top of the weights so it’s easier to evolve weights that change together or in opposition?
  • Rust gets great performance by giving control over memory management without the dangers of broken pointers and such. Apparently it’s the new Go and the syntax used to accomplish this is very interesting. Do people make simulations and games in Rust?
  • Why don’t the boids learn to get food more consistently? I was getting terrible outcomes from running 30 generations two or three times.
  • Can I run it hundreds of generations or thousands of generations?
  • Why does it sometimes freeze when I let it run for a while and then hit fast forward?

Also thinking I’d like to set up small multiples of performance indicators over 30-100 generations and vary some of the parameters. I want to see how these parameters affect the outcomes. Things like the number of generations, the mutation amount, the number of boids selected, the population and number of food sources, …I’m sure I can think of more.

If I can get a better feel for how these affect performance it could help me design scenarios which produce the desired outcome more effectively and which get there more efficiently.