xxxxxxxxxx
148
// Wolfram Elementary CA
// The Coding Train / Daniel Shiffman
// https://thecodingtrain.com/challenges/179-wolfram-ca
// https://youtu.be/Ggxt06qSAe4
// Array to store the state of each cell.
let cells = [];
// Rule value
let ruleValue = 18;
// The ruleset string
let ruleSet;
// Width of each cell in pixels
let w = 2;
// y-position
let y = 0;
let rowCtr = -1;
let rowToDebug = -1; // set to some positive number to get row data in the console.
let debug = false; // setting to true will display the contents of the state instead of the colors.
function setup() {
createCanvas(800, 800);
// Convert the rule value to a binary string.
ruleSet = ruleValue.toString(2).padStart(8, "0");
//console.log(ruleSet);
// Calculate the total number of cells based on canvas width.
let total = width / w;
// Initialize all cells to state 0 (inactive).
for (let i = 0; i < total; i++) {
cells[i] = 0;
}
// Set the middle cell to state 1 (active) as the initial condition.
cells[floor(total / 2)] = 1;
}
function draw() {
rowCtr++;
// Draw each cell based on its state.
for (let i = 0; i < cells.length; i++) {
let x = i * w;
noStroke();
fill("black");
if (debug) {
text(cells[i], x + 20, y + 20);
} else {
if (cells[i] > 0) {
// fill color based off of cell state
customfill(cells[i], w);
}
square(x, y, w);
}
}
// Move to the next row.
y += w;
// Prepare an array for the next generation of cells.
let nextCells = [];
// Iterate over each cell to calculate its next state.
let len = cells.length;
for (let i = 0; i < len; i++) {
// Calculate the states of neighboring cells
let left = cells[(i - 1 + len) % len];
let right = cells[(i + 1) % len];
let state = cells[i];
if (rowCtr == rowToDebug) {
console.log("Col " + i + " " + left + " " + right + " " + state);
}
// Set the new state based on the current state and neighbors.
let newState = calculateState(left, state, right, rowCtr + 1);
if (rowCtr == rowToDebug) {
console.log(newState);
}
if (newState == 0) {
nextCells[i] = 0;
} else {
// if a newState of 1 was returned, we fill the new cell with its
// existing value if it wasn't 0. If it had something other than 0,
// we keep that value.
if (rowCtr == rowToDebug) {
console.log(nextCells[i]);
}
nextCells[i] = state > 0 ? state : rowCtr + 2;
if (rowCtr == rowToDebug) {
console.log(nextCells[i]);
}
}
}
//rowCtr++;
// Update the cells array for the next generation.
cells = nextCells;
}
function calculateState(a, b, c) {
// *NS: anything more than 0 counts as 1 since cells represent
// the first time they were 1 without having been switched to 0.
a = a > 0 ? 1 : 0;
b = b > 0 ? 1 : 0;
c = c > 0 ? 1 : 0;
// Create a string representing the state of the cell and its neighbors.
let neighborhood = "" + a + b + c;
// Convert the string to a binary number
let value = 7 - parseInt(neighborhood, 2);
// Return the new state based on the ruleset.
return parseInt(ruleSet[value]);
}
function customfill(value, wi) {
// fills a color based off of cell value.
// starts by calculating a gradualness; the lower the width of the cells,
// the more gradual the change of colors can be and vice-versa.
// (mostly generated by AI)
gradualness = 255 / wi;
let red, green, blue;
// Normalize the input to the range [0, 3)
let normalizedValue = value % gradualness;
let segment = Math.floor(value / gradualness);
switch (segment) {
case 0: // Red to Yellow (increase Green)
red = 255;
green = Math.floor((normalizedValue / gradualness) * 255);
blue = 0;
break;
case 1: // Yellow to Cyan (decrease Red, increase Blue)
red = Math.floor(((gradualness - normalizedValue) / gradualness) * 255);
green = 255;
blue = Math.floor((normalizedValue / gradualness) * 255);
break;
case 2: // Cyan to Magenta (decrease Green, increase Red)
red = Math.floor((normalizedValue / gradualness) * 255);
green = Math.floor(((gradualness - normalizedValue) / gradualness) * 255);
blue = 255;
break;
}
fill(red, green, blue);
}