Cellular Automata - Instructions

A visual state simulation for practicing generated behaviour testing, JavaScript console exploration, and browser-based game hacking.

About Page

Cellular Automata is useful for exploring visual state, rules, and generated behaviour over time.

Use it to practice:

  • modelling visual state transitions
  • changing rule inputs
  • observing repeatability
  • inspecting JavaScript state
  • writing small console bots
  • controlling simulation setup and teardown
  • deciding what can be asserted in a visual simulation

Overview And Videos

These instructions are based on the Hacking JavaScript Games - Cellular Automata overview, which includes supporting notes and videos.

Supporting videos:

Exploratory Testing

Change one variable at a time and record how the visual behaviour changes. If a seed or rule can be repeated, compare repeated runs.

Console Hacking Exercises

The page exposes the simulation objects globally, so the browser console is the main testing tool.

Start by finding these objects and functions:

  • world
  • worldSizes
  • addLifeForms
  • startGame
  • stopGame
  • startInvasion
  • stopInvasion
  • clearCanvas
  • LifeForm
  • getRandomInt

Change Colour

Change the living-cell colour:

world.entityColour = "#111111";

Create a colour bot:

var colourChangeBot = setInterval(function() {
    world.entityColour = "#" + (("000000" +
        getRandomInt(0, 0xFFFFFF).toString(16)).substr(-6));
}, 1000);

Stop it with:

clearInterval(colourChangeBot);

You can make the bot faster or slower by changing the interval. Be careful with very fast colour changes because they can create flashing visuals.

Control The Invasion

Stop the current invasion:

stopInvasion();

Start a new invasion every 100 milliseconds:

startInvasion(100);

Add a single glider:

addLifeForms.glider(world, 10, 0);

Create a random glider invasion:

var gliderInvasion = setInterval(function() {
    addLifeForms.glider(world,
        getRandomInt(0, world.xSize - 50),
        getRandomInt(0, world.ySize - 50));
}, 100);

Stop it with:

clearInterval(gliderInvasion);

Change World Size

The rendered size and the simulation size are related but separate. Changing only worldSizes changes scale calculations, but the world also needs to know its new dimensions.

Try a small black-and-white world:

worldSizes.myWorldWidth = 64;
worldSizes.myWorldHeight = 48;
world.xSize = worldSizes.myWorldWidth;
world.ySize = worldSizes.myWorldHeight;
world.entityColour = "#000000";
worldSizes.calculateScales();

stopInvasion();
stopGame();
startGame(1000);
startInvasion(3000);

Then try a larger world and watch the performance impact:

worldSizes.myWorldWidth = 1000;
worldSizes.myWorldHeight = 1000;
world.xSize = 1000;
world.ySize = 1000;
worldSizes.calculateScales();

Create A Random World

Take control of the running simulation:

stopGame();
stopInvasion();
world.population = [];
clearCanvas();

Seed random life forms:

for (var itemLoop = 0; itemLoop < 500; itemLoop++) {
    world.addToPopulation(new LifeForm().move(0,
        getRandomInt(0, world.xSize),
        getRandomInt(0, world.ySize)));
}

Start the simulation:

startGame(5);

Create Text As Life

One way to create custom patterns is to draw text on the canvas, read back the pixels, and turn those pixels into life forms.

stopInvasion();
stopGame();
world.population = [];
clearCanvas();

context.font = "14px verdana";
context.strokeStyle = "#FF0000";

var padding = String.fromCharCode(8202) + String.fromCharCode(8202);
context.strokeText("TEST".split("").join(padding), 0, 15);
context.strokeText("PAGES".split("").join(padding), 0, 30);

Convert the visible pixels to blocks:

var imageData = context.getImageData(0, 0, 200, 60);
var scale = 4;
var imageX = 0;
var imageY = 0;

for (var i = 0; i < imageData.data.length; i += 4) {
    if (imageData.data[i] !== 0 ||
        imageData.data[i + 1] !== 0 ||
        imageData.data[i + 2] !== 0) {
        addLifeForms.block(world, imageX * scale, imageY * scale);
    }

    imageX++;

    if (imageX >= 200) {
        imageX = 0;
        imageY++;
    }
}

startGame(5);

Hey, did you notice you can add #interactive on the url? What does that do? Does that change your test approach now?