Skip to content

Instantly share code, notes, and snippets.

@PaulRB
Created May 1, 2016 17:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PaulRB/ea998292b684980a350462b9297dde6c to your computer and use it in GitHub Desktop.
Save PaulRB/ea998292b684980a350462b9297dde6c to your computer and use it in GitHub Desktop.
Conway's Game Of Life for WeMos D1 Mini
// Conway's Game Of Life
// For WeMos D1 Mini and 128x64 OLED display
// PaulRB
// April 2016
#include <SPI.h>
//Pins controlling SSD1306 Graphic OLED
#define OLED_DC D8
#define OLED_COLS 128
#define OLED_ROWS 64
SPISettings ssd1306(100000000UL, MSBFIRST, SPI_MODE0);
#define MATRIX_COLS 512
#define MATRIX_ROWS 512
union MatrixData {
unsigned long long l[MATRIX_ROWS/8/8];
byte b[MATRIX_ROWS/8];
};
MatrixData Matrix[MATRIX_COLS+1]; // Cell data in ram
int leftChanges, rightChanges, topChanges, bottomChanges;
void setup() {
byte startupCodes[] = {
0xAE, // Display off
0xD5, // Set display clock divider
0x80,
0xA8, // Set multiplex
0x3F,
0xD3, // Set display offset
0x00,
0x40, // Set start line to zero
0x8D, // Set charge pump
0x14,
0x20, // Set memory mode
0x00,
0xA1, // Set segment remapping
0xC8, // Set command Scan decode
0xDA, // Set Comm pins
0x12,
0x81, // Set contrast
0xCF,
0xd9, // Set precharge
0xF1,
0xDB, // Set Vcom detect
0x40,
0xA4, // Allow display resume
0xA6, // Set normal display
0xAF // Display On
};
Serial.begin(115200);
pinMode(OLED_DC, OUTPUT);
//Initialise the SSD1306 display
SPI.begin();
digitalWrite(OLED_DC, LOW); //Command mode
SPI.beginTransaction(ssd1306);
for (int i = 0; i < sizeof(startupCodes); i++) SPI.transfer(startupCodes[i]);
SPI.endTransaction();
//R-pentomino
//Matrix[127].l[4] = 0b0000010;
//Matrix[128].l[4] = 0b0000111;
//Matrix[129].l[4] = 0b0000100;
//injectGlider();
//Gosper's Glider Gun
//Matrix[64].l[4] = 0b00000000000000000000000000010000000000000000000000;
//Matrix[65].l[4] = 0b00000000000000000000000001010000000000000000000000;
//Matrix[66].l[4] = 0b00000000000000011000000110000000000001100000000000;
//Matrix[67].l[4] = 0b00000000000000100010000110000000000001100000000000;
//Matrix[68].l[4] = 0b00011000000001000001000110000000000000000000000000;
//Matrix[69].l[4] = 0b00011000000001000101100001010000000000000000000000;
//Matrix[70].l[4] = 0b00000000000001000001000000010000000000000000000000;
//Matrix[71].l[4] = 0b00000000000000100010000000000000000000000000000000;
//Matrix[72].l[4] = 0b00000000000000011000000000000000000000000000000000;
randomiseMatrix();
outputMatrix();
}
void loop() {
unsigned long start = millis();
for (int i=0; i<1000; i++) {
generateMatrix();
if (leftChanges - rightChanges > 16) scrollRight();
if (rightChanges - leftChanges > 16) scrollLeft();
if (bottomChanges - topChanges > 16) scrollDown();
if (topChanges - bottomChanges > 16) scrollUp();
outputMatrix();
yield();
}
Serial.print("Gens/s:"); Serial.println(1000000UL/(millis() - start));
}
void outputMatrix() {
digitalWrite(OLED_DC, LOW); //Command mode
SPI.beginTransaction(ssd1306);
SPI.transfer(0x21); // Set column address
SPI.transfer(0);
SPI.transfer(OLED_COLS-1);
SPI.transfer(0x22); // Set page address
SPI.transfer(0);
SPI.transfer(OLED_ROWS/8-1);
digitalWrite(OLED_DC, HIGH); // Data mode
//Send matrix data for display on OLED
for (int col = MATRIX_ROWS/8/2 - OLED_ROWS/8/2; col < MATRIX_ROWS/8/2 + OLED_ROWS/8/2; col++) {
for (int row = MATRIX_COLS/2 - OLED_COLS/2; row < MATRIX_COLS/2 + OLED_COLS/2; row++) {
SPI.transfer(Matrix[row].b[col]);
}
}
SPI.endTransaction();
}
void randomiseMatrix() {
//Set up initial cells in matrix
randomSeed(analogRead(A0));
for (int row = 0; row < MATRIX_COLS; row++) {
for (int col = 0; col < MATRIX_ROWS/8; col++) {
Matrix[row].b[col] = random(0xff) & random(0xff) & random(0xff);
}
}
}
void injectGlider() {
byte glider[4][3] = {
{ 0b0000111,
0b0000001,
0b0000010 },
{ 0b0000010,
0b0000001,
0b0000111 },
{ 0b0000011,
0b0000101,
0b0000001 },
{ 0b0000110,
0b0000101,
0b0000100 }
};
Serial.println("Inecting...");
int col = MATRIX_COLS/2;
int row = MATRIX_ROWS/8/2;
int dir = random(4);
Matrix[col++].b[row] |= glider[dir][0];
Matrix[col++].b[row] |= glider[dir][1];
Matrix[col++].b[row] |= glider[dir][2];
}
void rotateLeft(unsigned long long x[]) {
unsigned long long carryIn = 0, carryOut;
for (int i=0; i<MATRIX_ROWS/8/8; i++) {
carryOut = x[i] >> 63;
x[i] = x[i] << 1 | carryIn;
carryIn = carryOut;
}
x[0] |= carryOut;
}
void rotateRight(unsigned long long x[]) {
unsigned long long carryIn = 0, carryOut;
for (int i=MATRIX_ROWS/8/8-1; i>=0; i--) {
carryOut = x[i] << 63;
x[i] = x[i] >> 1 | carryIn;
carryIn = carryOut;
}
x[3] |= carryOut;
}
void scrollLeft() {
Matrix[MATRIX_COLS] = Matrix[0];
for (int row = 0; row < MATRIX_COLS+1; row++) Matrix[row] = Matrix[row+1];
}
void scrollRight() {
for (int row = MATRIX_COLS; row > 0; row--) Matrix[row] = Matrix[row-1];
Matrix[0] = Matrix[MATRIX_COLS];
}
void scrollUp() {
for (int row = 0; row < MATRIX_COLS; row++) rotateLeft(Matrix[row].l);
}
void scrollDown() {
for (int row = 0; row < MATRIX_COLS; row++) rotateRight(Matrix[row].l);
}
int generateMatrix() {
//Variables holding data on neighbouring cells
MatrixData NeighbourN, NeighbourNW, NeighbourNE, CurrCells, NeighbourW, NeighbourE, NeighbourS, NeighbourSW, NeighbourSE;
//Variables used in calculating new cells
unsigned long long tot1, carry, tot2, tot4, NewCells;
int changes = 0; // counts the changes in the matrix
static int prevChanges[4]; // counts the changes in the matrix on prev 4 generations
static int staleCount = 0; // counts the consecutive occurrances of the same number of changes in the matrix
leftChanges = rightChanges = topChanges = bottomChanges = 0;
//set up N, NW, NE, W & E neighbour data
NeighbourN = Matrix[MATRIX_COLS-1];
CurrCells = Matrix[0];
Matrix[MATRIX_COLS] = CurrCells; // copy row 0 to location after last row to remove need for wrap-around code in the loop
NeighbourNW = NeighbourN;
rotateLeft(NeighbourNW.l);
NeighbourNE = NeighbourN;
rotateRight(NeighbourNE.l);
NeighbourW = CurrCells;
rotateLeft(NeighbourW.l);
NeighbourE = CurrCells;
rotateRight(NeighbourE.l);
//Process each row of the matrix
for (int row = 0; row < MATRIX_COLS; row++) {
//Pick up new S, SW & SE neighbours
NeighbourS = Matrix[row + 1];
NeighbourSW = NeighbourS;
rotateLeft(NeighbourSW.l);
NeighbourSE = NeighbourS;
rotateRight(NeighbourSE.l);
for (int i=0; i<MATRIX_ROWS/8/8; i++) {
//Count the live neighbours (in parallel) for the current row of cells
//However, if total goes over 3, we don't care (see below), so counting stops at 4
tot1 = NeighbourN.l[i];
tot2 = tot1 & NeighbourNW.l[i]; tot1 = tot1 ^ NeighbourNW.l[i];
carry = tot1 & NeighbourNE.l[i]; tot1 = tot1 ^ NeighbourNE.l[i]; tot4 = tot2 & carry; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourW.l[i]; tot1 = tot1 ^ NeighbourW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourE.l[i]; tot1 = tot1 ^ NeighbourE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourS.l[i]; tot1 = tot1 ^ NeighbourS.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSW.l[i]; tot1 = tot1 ^ NeighbourSW.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
carry = tot1 & NeighbourSE.l[i]; tot1 = tot1 ^ NeighbourSE.l[i]; tot4 = tot2 & carry | tot4; tot2 = tot2 ^ carry;
//Calculate the updated cells:
// <2 or >3 neighbours, cell dies
// =2 neighbours, cell continues to live
// =3 neighbours, new cell born
NewCells = (CurrCells.l[i] | tot1) & tot2 & ~ tot4;
//Have any cells changed?
if (NewCells != CurrCells.l[i]) {
//Count the change for "stale" test
changes++;
if (row >= MATRIX_COLS/2) rightChanges++; else leftChanges++;
if (i >= MATRIX_ROWS/8/16) bottomChanges++; else topChanges++;
Matrix[row].l[i] = NewCells;
}
}
//Current cells (before update), E , W, SE, SW and S neighbours become
//new N, NW, NE, E, W neighbours and current cells for next loop
NeighbourN = CurrCells;
NeighbourNW = NeighbourW;
NeighbourNE = NeighbourE;
NeighbourE = NeighbourSE;
NeighbourW = NeighbourSW;
CurrCells = NeighbourS;
}
if (changes != prevChanges[0] && changes != prevChanges[1] && changes != prevChanges[2] && changes != prevChanges[3]) {
staleCount = 0;
}
else {
staleCount++; //Detect "stale" matrix
}
if (staleCount > 64) injectGlider(); //Inject a glider
for (int i=3; i>0; i--) {
prevChanges[i] = prevChanges[i-1];
}
prevChanges[0] = changes;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment