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:
- Change Colour of Cells
- Change Colour Automatically
- Create a Glider Invasion
- Change Size of the World
- Create a Randomly Generated World
- History Of The Game of Life
- Bonus: Making Text With Cellular Automata
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:
worldworldSizesaddLifeFormsstartGamestopGamestartInvasionstopInvasionclearCanvasLifeFormgetRandomInt
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?