So far I have this:
const rand = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
// rule:
// if an even number of nodes are touching
// the center node, then flip
// else skip.
// rule:
// for the start nodes (top row),
// they are randomly turned on and off.
const cellularAutomaton = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
]
const update = () => {
let int = rand(0, 7)
let row = cellularAutomaton[0]
row[int] = row[int] ? 0 : 1
// now, how to update the remaining rows?
for (let i = 1, n = cellularAutomaton.length; i < n; i++) {
row = cellularAutomaton[i]
for (let k = 0, m = row.length; k < m; k++) {
let neighbors = surrounding(i, k)
let count = sum(neighbors)
if (even(count)) {
row[k] = row[k] == 1 ? 0 : 1
}
}
}
}
const even = value => value % 2 == 0
const sum = neighbors => {
let sum = 0
let u = 0
while (u < 3) {
let v = 0
while (v < 3) {
let value = neighbors[u][v]
if (value != null) {
sum += value
}
v++
}
u++
}
return sum
}
const surrounding = (row, column) => {
let prevRow = cellularAutomaton[row - 1]
let nextRow = cellularAutomaton[row + 1]
let left = cellularAutomaton[row][column - 1]
let right = cellularAutomaton[row][column + 1]
let neighbors = []
if (prevRow) {
let a = prevRow[column - 1]
let b = prevRow[column]
let c = prevRow[column + 1]
neighbors.push([a, b, c])
} else {
neighbors.push([null, null, null])
}
neighbors.push([left, null, right])
if (nextRow) {
let a = nextRow[column - 1]
let b = nextRow[column]
let c = nextRow[column + 1]
neighbors.push([a, b, c])
} else {
neighbors.push([null, null, null])
}
return neighbors
}
let i = 0
const watch = () => {
i++
if (i == 100) {
clearInterval(interval)
}
}
const render = () => {
let out = []
let a = '■'
let b = '□'
for (let i = 0, n = cellularAutomaton.length; i < n; i++) {
row = cellularAutomaton[i]
let outrow = []
for (let k = 0, m = row.length; k < m; k++) {
let value = row[k]
if (value) {
outrow.push(a)
} else {
outrow.push(b)
}
}
out.push(outrow.join(' '))
out.push('\n')
}
console.clear()
console.log(out.join(''))
}
const draw = () => {
update()
watch()
render()
}
const interval = setInterval(draw, 1000)
Am I doing this correctly? I have never seen an implementation of cellular automata and so don't know if there is a more efficient way of accomplishing it. Please let me know how to improve this. What can be done differently or more effectively? In particular, finding the neighbors I'm not sure if I've done that well. Also, what do 2D cellular automata typically do on the edges? Do they wrap them to the other side? So everything has the same number of neighbors? I'm not sure exactly how the theory maps to code, and if in theory the neighbors number is the same for every box.
Note: I have in this CA a random "seed" in the first row to initialize values. That is by design, I want to "play a beat to the automaton" essentially, and this random function seed is just a pretend beat (without getting too fancy for this post).
As a side note, if you have any suggestions on inspiration on the types of rules that can be created, please leave a comment, I would like to build arbitrary CAs and not sure where to start.