From MathWorld: “A cellular automaton is a collection of ‘colored’ cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells.” This example explores binary, nearest-neighbor, one-dimensional automata, of which there are 256 (28) possible rules. The eight possible outcomes for the current rule are shown across the top; click to toggle the selected bit or drag the slider to jump to the desired rule.
WARNING: Moving the slider may produce flashing patterns!
Next: Belousov-Zhabotinsky
<html>
<head>
<title>Automaton Explorer</title>
<link rel="stylesheet" type="text/css" href="ex.css"/>
<script type="text/javascript" src="../protovis-r3.2.js"></script>
<script type="text/javascript" src="../jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../jquery-ui-1.8rc3.custom.min.js"></script>
<script type="text/javascript" src="cell.js"></script>
<link type="text/css" href="../ui-lightness/jquery-ui-1.8rc3.custom.css" rel="stylesheet"/>
<style type="text/css">
.ui-slider {
font-size: 10px;
width: 300px;
margin-top: 5px;
}
sup, sub {
line-height: 0;
}
.ui-state-focus {
outline: none;
}
#slider {
width: 300px;
display: inline-block;
margin-left: 5px;
margin-right: 15px;
}
#fig {
width: 768px;
height: 460px;
}
</style>
</head>
<body><div id="center"><div id="fig">
<div style="width:8in;">
<b>Rule:</b> <span id="slider"></span><span id="rule">30</span>
<span style="float:right;">
<b>Start:</b> <input type="radio" checked id="point" name="start" value="point"
><label for="point">point</label>
<input type="radio" id="random" name="start" value="random"
><label for="random">random</label>
</span>
</div><p>
<script type="text/javascript+protovis">
var h = 128,
w = 2 * h,
start = "point",
rule = 30;
var vis = new pv.Panel()
.width(w * 3)
.height(h * 3)
.top(40);
vis.add(pv.Panel)
.data(pv.range(8))
.right(function(i) (i + .5) * w * 3 / 8)
.top(-35)
.event("mousedown", function(i)
($("#rule").html(rule ^= 1 << i),
$(slider).slider('value', rule),
vis))
.add(pv.Bar)
.top(10)
.right(-5)
.width(10)
.height(10)
.title(function(j, i) "Toggle bit " + i + ".")
.strokeStyle("#bbb")
.fillStyle(function(i) rule >> i & 1 ? "#000" : "#fff")
.add(pv.Bar)
.data(pv.range(3))
.top(0)
.right(function(i) i * 10 - 15)
.fillStyle(function(i, j) j >> i & 1 ? "#000" : "#fff");
vis.add(pv.Image)
.def("cell", cell)
.imageWidth(w)
.imageHeight(h)
.image(pv.colors(null, "#000").by(function(x, y) this.cell()[y][x]));
vis.render();
$(slider).slider({
min: 1, value: 30, max: 255, slide: function(e, ui) {
$("#rule").html(rule = ui.value);
vis.render();
}
});
$([point, random]).change(function() {
start = this.value;
vis.render();
});
</script>
</div></div></body>
</html>
/** Depends on globals: rule, w, h, mode. */
function cell() {
var d = pv.range(h).map(function() {
return pv.range(w).map(function() { return 0; });
}),
r = pv.range(8).map(function(i) {
return rule >> i & 1;
});
if (start == "point") {
d[0][w >> 1] = 1;
} else {
for (var x = 0; x < w; x++) {
d[0][x] = cell.random(x);
}
}
for (var y = 1; y < h; y++) {
var p = d[y - 1], c = d[y];
for (var x = 0; x < w; x++) {
c[x] = r[p[x - 1] << 2 | p[x] << 1 | p[x + 1]];
}
}
return d;
}
cell.$random = {};
/** Caches random output to make exploration deterministic. */
cell.random = function(i) {
return i in cell.$random ? cell.$random[i]
: (cell.$random[i] = Math.random() < .5 ? 0 : 1);
};