Skip to content

Instantly share code, notes, and snippets.

@sutaburosu
Created May 31, 2021 10:10
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 sutaburosu/6e8f886edcafa5cc62189e6182075866 to your computer and use it in GitHub Desktop.
Save sutaburosu/6e8f886edcafa5cc62189e6182075866 to your computer and use it in GitHub Desktop.
Conway's Life on SmartMatrix
// Conway's Game of Life - rule 23/3
// originally by jimLee
// https://forum.arduino.cc/t/the-fastest-way-to-move-a-block-of-ram/865841
uint32_t seed = 321524;
#define GRID_X 128
#define GRID_Y 64
#define TRAILS true
#define SM_INTERNAL
#include <MatrixHardware_Teensy4_ShieldV5_RBG.h>
#include <SmartMatrix.h>
#define COLOR_DEPTH 24 // Choose the color depth used for storing pixels in the layers: 24 or 48 (24 is good for most sketches - If the sketch uses type `rgb24` directly, COLOR_DEPTH must be 24)
const uint16_t kMatrixWidth = GRID_X; // Set to the width of your display, must be a multiple of 8
const uint16_t kMatrixHeight = GRID_Y; // Set to the height of your display
const uint8_t kRefreshDepth = 36; // Tradeoff of color quality vs refresh rate, max brightness, and RAM usage. 36 is typically good, drop down to 24 if you need to. On Teensy, multiples of 3, up to 48: 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48. On ESP32: 24, 36, 48
const uint8_t kDmaBufferRows = 2; // known working: 2-4, use 2 to save RAM, more to keep from dropping frames and automatically lowering refresh rate. (This isn't used on ESP32, leave as default)
const uint8_t kPanelType = SM_PANELTYPE_HUB75_64ROW_MOD32SCAN; // Choose the configuration that matches your panels. See more details in MatrixCommonHub75.h and the docs: https://github.com/pixelmatix/SmartMatrix/wiki
SMARTMATRIX_ALLOCATE_BUFFERS(matrix, kMatrixWidth, kMatrixHeight, kRefreshDepth, kDmaBufferRows, kPanelType, 0);
SMARTMATRIX_ALLOCATE_BACKGROUND_LAYER(backgroundLayer, kMatrixWidth, kMatrixHeight, COLOR_DEPTH, 0);
const byte BYTES_X = GRID_X / 8;
const uint16_t bytes = BYTES_X * GRID_Y;
byte grid[GRID_Y][BYTES_X];
byte tempGrid[GRID_Y][BYTES_X];
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("Hello?");
matrix.addLayer(&backgroundLayer);
matrix.begin();
backgroundLayer.setBrightness(128);
randomFill((byte *) grid, seed);
}
void randomFill(byte *gPtr, uint32_t seed) {
Serial.print("Seed: ");
Serial.print(seed);
randomSeed(seed);
for (int i = 0; i < GRID_X / 8 * GRID_Y; i++) {
*gPtr++ = random(255) & random(255) & random(255);
}
paintRasterGrid(grid, false);
}
void tempToGrid(void) {
byte* gPtr;
byte* tPtr;
gPtr = (byte*) grid;
tPtr = (byte*) tempGrid;
memcpy(gPtr, tPtr, bytes);
}
void setGrid(byte grid[][BYTES_X], int x, int y, bool value) {
int xIndex;
byte xBit;
if (x < 0 || x >= GRID_X) return;
if (y < 0 || y >= GRID_Y) return;
xIndex = x >> 3;
// xBit = x - (xIndex << 3);
xBit = x & 7;
if (value) {
bitSet(grid[y][xIndex], xBit);
} else {
bitClear(grid[y][xIndex], xBit);
}
}
bool getGrid(byte grid[][BYTES_X], int x, int y) {
int xIndex;
byte xBit;
bool result;
result = false;
if (x < 0 || x >= GRID_X) return result;
if (y < 0 || y >= GRID_Y) return result;
xIndex = x >> 3;
// xBit = x - (xIndex << 3);
xBit = x & 7;
result = (bool) bitRead(grid[y][xIndex], xBit);
return result;
}
// Any live cell with two or three live neighbours survives.
// Any dead cell with three live neighbours becomes a live cell.
// All other live cells die in the next generation.
// Similarly, all other dead cells stay dead.
bool updatePoint(int x, int y) {
byte numLiveCells;
int xMinus;
int xPlus;
numLiveCells = 0;
xMinus = x - 1;
xPlus = x + 1;
y--;
numLiveCells = numLiveCells + (int)getGrid(grid, xMinus, y);
numLiveCells = numLiveCells + (int)getGrid(grid, x, y);
numLiveCells = numLiveCells + (int)getGrid(grid, xPlus, y);
y++;
numLiveCells = numLiveCells + (int)getGrid(grid, xMinus, y);
numLiveCells = numLiveCells + (int)getGrid(grid, xPlus, y);
y++;
numLiveCells = numLiveCells + (int)getGrid(grid, xMinus, y);
numLiveCells = numLiveCells + (int)getGrid(grid, x, y);
numLiveCells = numLiveCells + (int)getGrid(grid, xPlus, y);
y--;
if (getGrid(grid, x, y)) {
if (numLiveCells == 2 || numLiveCells == 3) {
return true;
} else {
return false;
}
} else {
if (numLiveCells == 3) {
return true;
}
}
return false;
}
void paintRasterGrid(byte src[][BYTES_X], bool blue) {
byte *grid = (byte *) src;
for (uint16_t y = 0; y < GRID_Y; y++) {
for (uint16_t x = 0; x < GRID_X; x += 8) {
byte gridval = *grid++;
for (uint8_t bitpos = 0; bitpos < 8; bitpos++) {
if (gridval & 1) {
backgroundLayer.drawPixel(x+bitpos, y, rgb24(255, 255, 255));
} else {
if (blue) {
rgb24 tmp = backgroundLayer.readPixel(x+bitpos, y);
backgroundLayer.drawPixel(x+bitpos, y, rgb24(0, 0, tmp.blue > 64 ? tmp.blue - 1 : tmp.blue));
} else {
backgroundLayer.drawPixel(x+bitpos, y, rgb24(0, 0, 0));
}
}
gridval >>= 1;
}
}
}
}
// a terrible hash function, to quickly distinguish similar frames
uint32_t hash(byte * src, uint16_t length) {
uint32_t total = 0;
while (length--)
total = (total ^ (total << 13)) + *src++ * (length & 0x3f);
return total;
}
void stepTime(void) {
bool result;
for (int y = 0; y < GRID_Y; y++) {
for (int x = 0; x < GRID_X; x++) {
result = updatePoint(x, y);
setGrid(tempGrid, x, y, result);
}
}
// detect looping states, up to max_loop + 1 frames in length
const uint8_t max_loop = 14;
static uint32_t framehash[max_loop];
static uint16_t consecutives = 0;
static uint32_t seed_frames = 0;
seed_frames++;
uint32_t newhash = hash((byte *) tempGrid, bytes);
bool match = false;
for (uint8_t i = 0; i < max_loop; i++)
if (newhash == framehash[i])
match = true;
if (match) {
consecutives++;
if (consecutives >= 240) {
Serial.print(" frames: ");
Serial.println(seed_frames);
// start with the next seed
randomFill((byte *) tempGrid, ++seed);
seed_frames = 0;
}
} else
consecutives = 0;
for (uint8_t i = 0; i < max_loop - 1; i++)
framehash[i] = framehash[i+1];
framehash[max_loop - 1] = newhash;
}
void loop() {
backgroundLayer.swapBuffers(true);
while(backgroundLayer.isSwapPending());
stepTime();
paintRasterGrid(tempGrid, TRAILS);
tempToGrid();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment