Neural Network Cellular Automata

Okay. A random post on the /r/cellular_automata subreddit inspired me.

Let’s generate a cellular automata where each pixel updates based on a neural network given as input:

  • The x/y coordinates (scaled to the range 0-1)
  • An optional random value (to make it more dynamic)
  • A variety of neighboring data, such as:
    • The number of neighbors that are ‘active’ (> 50% white), ranges 0-8 scaled to 0-1. This should allow Conway's Game of Life
    • The RGB values of all neighbors (allows a superset of the above)
    • Gradients, subtract color value of the left from the right so that you get edges and side to side movement

Let’s do it!

read more...


Pictogenesis: Stack Transpiling

Much like transpiling register machines, now we have a chance to transpile stack machines. Unfortunately, it doesn’t actually speed up the code nearly so much (the stack is just not as effective of a memory structure in this case), but it’s still an interesting bit of code.

In this case, we turn something like this:

invsub
polT
writeG
id
neg
zero?
sin
invsub
ZERO
inv

Into this:

function(X, Y) {
  this.x = X;
  this.y = Y;

  this.stack = [];
  this.r = undefined;
  this.g = undefined;
  this.b = undefined;

  this.stack.push(X);
  this.stack.push(Y);

  var arg0 = 0;
  var arg1 = 0;
  var arg2 = 0;
  var result = 0;

  // invsub
  arg0 = this.stack.pop() || 0;
  result = 1 - arg0;
  result = result % 1.0;
  this.stack.push(result);

  // polT
  arg0 = this.stack.pop() || 0;
  arg1 = this.stack.pop() || 0;
  result = Math.atan2(arg0, arg1);
  result = result % 1.0;
  this.stack.push(result);

  // writeG
  arg0 = this.stack.pop() || 0;
  this.g = arg0;

  // id
  arg0 = this.stack.pop() || 0;
  result = arg0;
  result = result % 1.0;
  this.stack.push(result);

  // neg
  arg0 = this.stack.pop() || 0;
  result = -arg0;
  result = result % 1.0;
  this.stack.push(result);

  // zero?
  arg0 = this.stack.pop() || 0;
  arg1 = this.stack.pop() || 0;
  arg2 = this.stack.pop() || 0;
  result = arg0 === 0 ? arg1 : arg2;
  result = result % 1.0;
  this.stack.push(result);

  // sin
  arg0 = this.stack.pop() || 0;
  result = Math.sin(arg0);
  result = result % 1.0;
  this.stack.push(result);

  // invsub
  arg0 = this.stack.pop() || 0;
  result = 1 - arg0;
  result = result % 1.0;
  this.stack.push(result);

  // ZERO
  result = 0;
  result = result % 1.0;
  this.stack.push(result);

  // inv
  arg0 = this.stack.pop() || 0;
  result = 1 / arg0;
  result = result % 1.0;
  this.stack.push(result);


  return [
    this.r === undefined ? this.stack.pop() || 0 : this.r,
    this.g === undefined ? this.stack.pop() || 0 : this.g,
    this.b === undefined ? this.stack.pop() || 0 : this.b,
  ];
}

read more...


Pictogenesis: Stack Machine

Okay, enough with register machines. Let’s make something new. This time, a stack based machine!

Rather than keeping it’s memory in a series of memory cells, there will be a single stack of values. All functions can pop values from the top of the stack or push them back on. I will add the ability to read the X/Y value and directly write R/G/B, but you can’t write to the former or read from the latter, so you can’t use them as registers. Let’s see what that looks like!

read more...


Pictogenesis: Transpiling

Okay. That is slow… Let’s make it faster!

So the main problem we have is that we’re interpreting the code. For every single pixel, for every line of code, we’re doing a few housekeeping things and making at least one function call. For a 400x400 image with just 10 lines of code, that’s 1.6M function calls. Like I said, slow.

So let’s make it faster!

My first idea? Transpile it to Javascript!

read more...


Pictogenesis: Wrapping Modes

Now that I’ve got register machines working, one of the next ideas I had was to implement different wrapping modes. Currently, as it stands, X and Y are passed into the machine as floating point numbers from [0, 1] across the image and output is expected to be [0, 1] for each of R, G, and B. Any values that end up outside of that range, we truncate down to that range. But some of our mathematical functions (multiplication, exponentiation, negation, etc) tend to generate numbers way out of this range. But they don’t have to!

read more...


Pictogenesis: Register Machine

Okay. First Pictogeneis machine: a register based machine. Today we’re going to create a very small language with a small number of registers that can read from the outside world, write colors, and act as temporary variables.

Something like this:

gt? t0 b y x r
add g y x
abs b x
inv t0 g
add r g x
sub t0 b r
mul x r b
abs y x

In each case, the first argument is the output and the rest are inputs. So:

# gt? t0 b y x r
if (b > y) {
    t0 = x;
} else {
    t0 = r;
}
 
# add g y x
g = y + x

# abs b x
b = |x|
...

Where x and y are the input point x and y mapped to the range [0, 1]; r, g, b are the output colors in the same range and t{n} are temporary registers just used during the program.

read more...


Pictogenesis: The Idea

PICTOGENESIS REBORN!

I don’t know if I ever actually posted it publically, but one of the ideas I’ve had percolating for the longest time is combining tiny interpreters and genetic algorithms to make generative art.

The basic idea is to generate programs (in various styles) that can take x,y coordinates and return colors. Then apply that to every pixel on an image to make generative art. Once we have, figure out a way to mutate/breed the programs so that we can apply a genetic algorithm to them and make awesome images! Sort of like Electric Sheep (that brings back memories).

The evolution point of view was actually a pretty tricky problem, since programs can have a number of different representations. I could compile them to bytecode and mutate that, but how do I make most code at least potentially meaningful?

read more...


Backtracking Worm Coral

Let’s take yesterday’s Worm Coral and turn it up to 11!

Now we have:

  • Whenever a worm gets stuck, it will ‘backtrack’: it will instead expand from the previous position recursively

That means that the initial 10 worms should always be able to fill the entire world! Even if one closes off an area, that one can eventually fill it up:

I like how occasionally you get one spindly bit (usually early in the run) that another goes through. It reminds me of Blokus It does take a while.

In addition, I wanted to play a bit with simulationism:

  • Worms can potentially changeColor each frame
  • Every framesPerGeneration check if each worm dies deathChance or spawns a child worm (spawnChance)
  • If a worm dies, it is removed from the simulation
  • If a worm spawns, it creates a new child at it’s current location
    • If spawnIncludesHistory is set, the child can backtrack into the parent’s history
    • If spawnVariesColor is set, the child will (potentially, it’s random) have a slightly different color

Let’s check it out!

read more...


Worm Coral

Today, I’m going to work on using worms to generate coral, similar to what I did way back when I was generating omnichromatic images.

In a nutshell:

  • Spawn n worms
  • On each tick:
    • Each worm tries to randomly move one direction
    • If it cannot, increment that worm’s stuck counter
    • If it can, restart the stuck counter
    • If a worm is stuck long enough, kill it off and spawn a new worm

Eventually, we’ll fill the entire space with colors that end up looking a bit like coral. I’ll probably extend this later, since there are a lot of cool tweaks you can do with this general idea.

read more...


p5js Boids

Okay, sketch 2: Boids!

The basic idea is to create a bunch of particles (the Boids in this case) and apply to them each a series of simple, limited rules that rely neither on communcation between the Boids nor a global controller and see what behaviors you can generate. Specifically, can you replicate the flocking behavior found in birds, since birds can obviously fly together without hitting one another and also without some lead bird giving orders.

Something like this:

For this case, there are three rules:

  • seperation - Fly away from any Boids that are too close to you (to avoid collision)
  • alignment - Align yourself to fly in the same direction as any Boids in your field of vision
  • cohesion - Fly towards the center point of the Boids you can see

read more...