#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED display setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define GRID_WIDTH 8 // Smaller 8x8 grid for Game of Life
#define GRID_HEIGHT 8
#define CELL_SIZE 16 // Larger cell size to fit smaller grid
// Declaration for SSD1306 display
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Grids to store cell states
int grid[GRID_WIDTH][GRID_HEIGHT];
int nextGrid[GRID_WIDTH][GRID_HEIGHT];
// Part 1: Setup the OLED display and initialize the grid
void setup() {
Serial.begin(9600); // Start serial communication for debugging
// Initialize OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Loop forever if OLED setup fails
}
// Clear display at the start
display.clearDisplay();
display.display();
Serial.println(F("OLED Initialized"));
// Initialize the grid with random states
initializeGrid();
// Draw the initial grid state
drawGrid();
}
// Part 2: Initialize the grid with random cells
void initializeGrid() {
Serial.println(F("Initializing grid, with 33% live cells"));
for (int x = 0; x < GRID_WIDTH; x++) {
for (int y = 0; y < GRID_HEIGHT; y++) {
//33% cell alive (1); 67% chance cell is dead (0)
grid[x][y] = (random(3) == 0) ? 1 : 0;
Serial.print(F("grid["));
Serial.print(x);
Serial.print(F("]["));
Serial.print(y);
Serial.print(F("] = "));
Serial.println(grid[x][y]); // Print cell state for debugging
}
}
Serial.println(F("Grid initialization complete"));
}
// Part 3: Implement Game of Life rules and update the grid
void updateGrid() {
for (int x = 0; x < GRID_WIDTH; x++) {
for (int y = 0; y < GRID_HEIGHT; y++) {
int neighbors = countNeighbors(x, y);
// Apply the Game of Life rules
if (grid[x][y] == 1) {
if (neighbors < 2 || neighbors > 3) {
nextGrid[x][y] = 0; // Cell dies
} else {
nextGrid[x][y] = 1; // Cell survives
}
} else {
if (neighbors == 3) {
nextGrid[x][y] = 1; // Cell becomes alive
} else {
nextGrid[x][y] = 0; // Cell remains dead
}
}
}
}
// Copy nextGrid to grid
for (int x = 0; x < GRID_WIDTH; x++) {
for (int y = 0; y < GRID_HEIGHT; y++) {
grid[x][y] = nextGrid[x][y];
}
}
}
// Part 3: Count live neighbors around a cell
int countNeighbors(int x, int y) {
int sum = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
int col = (x + i + GRID_WIDTH) % GRID_WIDTH;
int row = (y + j + GRID_HEIGHT) % GRID_HEIGHT;
sum += grid[col][row];
}
}
sum -= grid[x][y]; // Remove the cell itself from the count
return sum;
}
// Part 4: Draw the grid on the OLED display
void drawGrid() {
display.clearDisplay(); // Clear the display before drawing
for (int x = 0; x < GRID_WIDTH; x++) {
for (int y = 0; y < GRID_HEIGHT; y++) {
if (grid[x][y] == 1) {
// Draw a filled square for live cells
display.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
} else {
// Optionally, draw empty squares for dead cells (for a grid effect)
display.drawRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
}
}
}
display.display(); // Show the updated grid on the screen
Serial.println(F("Grid drawn"));
}
// Part 5: Main loop, update the grid and display the next generation
void loop() {
drawGrid(); // Draw the current generation
updateGrid(); // Apply the Game of Life rules to get the next generation
delay(250); // Add a delay for visual effect
}