Skip to content

Instantly share code, notes, and snippets.

@feyderm
Last active April 23, 2017 22:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save feyderm/77146d973ddd33294d2da4e4ef380413 to your computer and use it in GitHub Desktop.
Save feyderm/77146d973ddd33294d2da4e4ef380413 to your computer and use it in GitHub Desktop.
Cellular automata
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect {
height: 10px;
width: 10px;
fill: lightgray;
}
.state_0 {
fill: lightgray;
}
.state_1 {
fill: red;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">
// CELLULAR AUTOMATA VISUALIZATION
//
// inspired by:
// - https://www.nature.com/nature/journal/v544/n7649/full/nature22031.html
//
// references:
// - https://bl.ocks.org/mbostock/70d5541b547cc222aa02
// - http://natureofcode.com/book/chapter-7-cellsular-automata/
addArrayEqualityMethod();
// initial params
let svg_dx = 800,
svg_dy = 800,
n_rows = 50,
n_cols = 75,
rows = d3.range(n_rows),
cols = d3.range(n_cols),
cells = d3.cross(rows, cols, (row, col) => {
return {"row": row, "col": col, "state": 0};
});
// set initial state of cell in first row, center col to 1
cells[Math.round(n_cols/2)].state = 1;
let svg = d3.select("body")
.append("svg")
.attr("width", svg_dx)
.attr("height", svg_dy);
// plot rects
svg.selectAll("rect")
.data(cells)
.enter()
.append("rect")
.attr("class", assignClass)
.attr("x", cell => cell.col * 11)
.attr("y", cell => cell.row * 11);
// update cell states for each row
rows.forEach(row => {
d3.selectAll(".row_" + row)
.call(plotStates, row)
});
function plotStates(selection, row) {
let old_states = selection.data().map(cell => cell.state),
new_states = computeNewState(old_states);
// update next row
// NB: arrow function does not bind 'this'
d3.selectAll(".row_" + (row + 1))
.each(function(cell, i) {
cell.state = new_states[i];
d3.select(this)
.transition()
.delay(() => row * 50)
.attr("class", assignClass);
});
}
function computeNewState(states) {
// state of first cell remains 0
let new_states = [0];
// NB: loop begins at second element in array and end at second
// to last element in array; state for first and last elements
// in array remain 0
for (let i = 1; i < states.length - 1; i++) {
// NB: slice end not included
let context = states.slice(i - 1, i + 2);
// rule 126
if (context.equals([1, 1, 1])) {
new_states.push(0);
} else if (context.equals([1, 1, 0])) {
new_states.push(1);
} else if (context.equals([1, 0, 1])) {
new_states.push(1);
} else if (context.equals([1, 0, 0])) {
new_states.push(1);
} else if (context.equals([0, 1, 1])) {
new_states.push(1);
} else if (context.equals([0, 1, 0])) {
new_states.push(1);
} else if (context.equals([0, 0, 1])) {
new_states.push(1);
} else if (context.equals([0, 0, 0])) {
new_states.push(0);
}
}
// state of last cell remains 0
new_states.push(0);
return new_states;
}
function assignClass(cell) {
return "row_" + cell.row + (cell.state == 1 ? " state_1" : " state_0");
}
function addArrayEqualityMethod() {
// from http://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript
//
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
for (var i = 0, l=this.length; i < l; i++) {
if (this[i] != array[i]) {
return false;
}
}
return true;
}
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment