/*
Explanation of Codd's Cellular Automaton:
Codd's cellular automaton is a variation of Conway's Game of Life, proposed by the computer scientist Edgar F. Codd.
In Codd's automaton, a cell survives to the next generation if it has 2 or 3 live neighbors. If a dead cell has 3 live neighbors,
it becomes alive in the next generation. All other cells die or remain dead in the next generation.
Rules:
- Any live cell with 2 or 3 live neighbors survives.
- Any dead cell with exactly 3 live neighbors becomes alive.
- All other live cells die in the next generation, and all other dead cells remain dead.
*/
#include <Arduino.h> // Include the Arduino core library
#include <stdlib.h> // Include the standard library for functions like random()
#define CLK 13 // Clock pin for the LED matrix modules
#define DIN 11 // Data input pin for the LED matrix modules
#define CS 10 // Chip select pin for the LED matrix modules
#define X_SEGMENTS 8 // Number of segments in the x direction
#define Y_SEGMENTS 4 // Number of segments in the y direction
#define NUM_SEGMENTS (X_SEGMENTS * Y_SEGMENTS) // Total number of segments
byte fb[8 * NUM_SEGMENTS]; // Current frame buffer for the LED matrix
byte nextFb[8 * NUM_SEGMENTS]; // Next frame buffer for the LED matrix
// Function to send data to all segments
void shiftAll(byte send_to_address, byte send_this_data) {
digitalWrite(CS, LOW); // Select the LED matrix modules
for (int i = 0; i < NUM_SEGMENTS; i++) { // Loop through each segment
shiftOut(DIN, CLK, MSBFIRST, send_to_address); // Send the address
shiftOut(DIN, CLK, MSBFIRST, send_this_data); // Send the data
}
digitalWrite(CS, HIGH); // Deselect the LED matrix modules
}
void setup() {
Serial.begin(115200); // Initialize serial communication
pinMode(CLK, OUTPUT); // Set clock pin as output
pinMode(DIN, OUTPUT); // Set data input pin as output
pinMode(CS, OUTPUT); // Set chip select pin as output
shiftAll(0x0f, 0x00); // Send shutdown command to LED matrix modules
shiftAll(0x0b, 0x07); // Set scan limit to display all segments
shiftAll(0x0c, 0x01); // Enable normal operation
shiftAll(0x0a, 0x0f); // Set intensity to maximum
shiftAll(0x09, 0x00); // Clear display
memset(fb, 0, sizeof(fb)); // Clear the current frame buffer
randomSeed(analogRead(0)); // Seed the random number generator
for (int i = 0; i < 400; i++) { // Initialize some random live cells
int x = random(0, X_SEGMENTS * 8); // Random x coordinate
int y = random(0, Y_SEGMENTS * 8); // Random y coordinate
set_pixel(x, y, true); // Set the pixel at (x, y) to be alive
}
}
void loop() {
updateCoddAutomaton(); // Update the Codd's Cellular Automaton grid
show(); // Display the current frame buffer
delay(0); // Delay to control the animation speed
}
void updateCoddAutomaton() {
for (int i = 0; i < 8 * NUM_SEGMENTS; i++) {
nextFb[i] = fb[i]; // Copy the current frame buffer to the next frame buffer
}
for (int y = 0; y < Y_SEGMENTS * 8; y++) {
for (int x = 0; x < X_SEGMENTS * 8; x++) {
int neighbors = countNeighbors(x, y); // Count the number of live neighbors
if (fb[y * X_SEGMENTS + x / 8] & (1 << (7 - (x % 8)))) { // Check if the current cell is alive
if (neighbors < 2 || neighbors > 3) {
set_next_pixel(x, y, false); // Set the next state of the current cell to dead
}
} else {
if (neighbors == 3) {
set_next_pixel(x, y, true); // Set the next state of the current cell to alive
}
}
}
}
for (int i = 0; i < 8 * NUM_SEGMENTS; i++) {
fb[i] = nextFb[i]; // Update the current frame buffer with the next frame buffer
}
}
int countNeighbors(int x, int y) {
int count = 0; // Initialize neighbor count
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue; // Skip the current cell
int nx = (x + dx + X_SEGMENTS * 8) % (X_SEGMENTS * 8); // Calculate neighbor x coordinate
int ny = (y + dy + Y_SEGMENTS * 8) % (Y_SEGMENTS * 8); // Calculate neighbor y coordinate
if (fb[ny * X_SEGMENTS + nx / 8] & (1 << (7 - (nx % 8)))) count++; // Check if neighbor is alive
}
}
return count; // Return the number of live neighbors
}
void set_next_pixel(uint8_t x, uint8_t y, bool state) {
byte *addr = &nextFb[x / 8 + y * X_SEGMENTS]; // Calculate the address of the pixel in the next frame buffer
byte mask = 128 >> (x % 8); // Calculate the bit mask for the pixel
if (state) {
*addr |= mask; // Set the pixel to be alive
} else {
*addr &= ~mask; // Set the pixel to be dead
}
}
void set_pixel(uint8_t x, uint8_t y, bool state) {
byte *addr = &fb[x / 8 + y * X_SEGMENTS]; // Calculate the address of the pixel in the current frame buffer
byte mask = 128 >> (x % 8); // Calculate the bit mask for the pixel
if (state) {
*addr |= mask; // Set the pixel to be alive
} else {
*addr &= ~mask; // Set the pixel to be dead
}
}
void clear() {
memset(fb, 0, sizeof(fb)); // Clear the current frame buffer
}
void show() {
for (byte row = 0; row < 8; row++) { // Loop through each row of the LED matrix
digitalWrite(CS, LOW); // Select the LED matrix modules
byte segment = NUM_SEGMENTS; // Initialize segment counter
while (segment--) { // Loop through each segment
byte x = segment % X_SEGMENTS; // Calculate the x coordinate of the segment
byte y = segment / X_SEGMENTS * 8; // Calculate the y coordinate of the segment
byte addr = (row + y) * X_SEGMENTS; // Calculate the address of the segment in the frame buffer
if (segment & X_SEGMENTS) { // Check if segment is odd
shiftOut(DIN, CLK, MSBFIRST, 8 - row); // Send row address for odd segments
shiftOut(DIN, CLK, LSBFIRST, fb[addr + x]); // Send segment data for odd segments
} else {
shiftOut(DIN, CLK, MSBFIRST, 1 + row); // Send row address for even segments
shiftOut(DIN, CLK, MSBFIRST, fb[addr - x + X_SEGMENTS - 1]); // Send segment data for even segments
}
}
digitalWrite(CS, HIGH); // Deselect the LED matrix modules
}
}