/**************************************************************************
This is a game of life
based on an SSD1306 drivers example
**************************************************************************/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <stdio.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define STEP_TIME 250
#define XDIM 128
#define YDIM 32
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const int n_pixel = XDIM * YDIM;
const int n_bytes = n_pixel / 8;
int last_time = 0;
uint8_t *DISPLAY_POINTER;
uint8_t *M; // pointer to the segment we want to control
uint8_t N[n_bytes]; // next state
const int selectPin = 6; // the value of this pin selects how we load
void setup() {
pinMode(selectPin, INPUT_PULLUP);
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(500);
display.dim( true );
delay(500);
DISPLAY_POINTER = display.getBuffer();
M = DISPLAY_POINTER;
if (digitalRead(selectPin)==HIGH) {
// Clear the buffer
// display.clearDisplay();
init_random(); // Initialize randomly
}
init_secondary_buffer();
display.display();
}
void loop() {
int t = millis();
if (t - last_time >= STEP_TIME) {
last_time = t;
step_GOL();
display.display();
}
}
// fill the space with random numbers
//
void init_random(void) {
for (int k=0; k<n_bytes; k++) {
M[k] = (uint8_t) rand();
}
}
// copy the state to the secondary buffer
//
void init_secondary_buffer (void) {
for (int k=0; k<n_bytes; k++) {
N[k] = M[k];
}
}
// Perform an update step in Conway's Game Of Life
//
void step_GOL(void) {
// 1. Any live cell with fewer than two live neighbours dies, as if by underpopulation.
// 2. Any live cell with two or three live neighbours lives on to the next generation.
// 3. Any live cell with more than three live neighbours dies, as if by overpopulation.
// 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
// These rules, which compare the behaviour of the automaton to real life, can be condensed into the following:
// 1. Any live cell with two or three live neighbours survives.
// 2. Any dead cell with three live neighbours becomes a live cell.
// 3. All other live cells die in the next generation. Similarly, all other dead cells stay dead.
for (int x=0; x<XDIM; x++) {
for (int y=0; y<YDIM; y++) {
bool is_alive, do_live;
int neighbors;
is_alive = get_state(x,y);
do_live = is_alive;
neighbors = get_neighbors(x, y);
if (is_alive == true) {
if (neighbors < 2 || neighbors > 3)
do_live = false;
}
else { // dead cell
if (neighbors == 3)
do_live = true;
}
set_state(x,y,do_live);
}
}
for (int k=0; k<n_bytes; k++) {
M[k] = N[k];
}
}
// Get the state of a pixel
//
bool get_state(int x, int y)
{
int ry = y / 8;
int dy = y % 8;
int byte_pos = x + ry * SCREEN_WIDTH;
uint8_t V = M[byte_pos];
uint8_t mask = 1 << dy;
bool val;
val = (mask & V) ? true : false;
return val;
}
// Set the state of a pixel
//
void set_state(int x, int y, bool state)
{
int ry = y / 8;
int dy = y % 8;
int byte_pos = x + ry * SCREEN_WIDTH;
uint8_t V = N[byte_pos]; // account for multiple rows
uint8_t mask = 1 << dy;
if (state)
N[byte_pos] = (V | mask); // force 1
else
N[byte_pos] = (V & ~mask);// force 0
}
// Count the live neighbors of a pixel
//
int get_neighbors(int x, int y)
{
int neighbors = 0;
int xk, yk;
for (int dx=-1; dx<=1; dx++) {
xk = x + dx;
if (xk >=0 && xk < XDIM) { // count only the x-range
for (int dy=-1; dy<=1; dy++) {
yk = y + dy;
if (yk >=0 && yk < YDIM) { // count only the y-range
if (dx != 0 || dy != 0) { // don't count the 0-0 position
if (get_state(xk, yk))
neighbors++;
}
}
}
}
}
return neighbors;
}
/*
Serial.print(x);
Serial.print(",");
Serial.print(y);
Serial.print(": s=");
Serial.print(state);
Serial.print(", V= ");
Serial.print(V);
Serial.print(", mask= ");
Serial.print(mask);
Serial.print(", N[x]= ");
Serial.println(N[x]);
*/