GoL - Optimizations
Uncapped update option if speed set to 255.
This commit is contained in:
159
wled00/FX.cpp
159
wled00/FX.cpp
@@ -5191,27 +5191,26 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
const size_t dataSize = ((SEGMENT.length() + 7) / 8); // round up to nearest byte
|
||||
const size_t detectionSize = sizeof(uint16_t) * 3; // 2 CRCs, gliderLength
|
||||
const size_t detectionSize = sizeof(uint16_t) * 3 + 1; // 2 CRCs, gliderLength, soloGlider boolean
|
||||
const size_t totalSize = dataSize * 2 + detectionSize + sizeof(uint8_t); // detectionSize + prevPalette
|
||||
|
||||
if (!SEGENV.allocateData(totalSize)) return mode_static(); //allocation failed
|
||||
byte *cells = reinterpret_cast<byte*>(SEGENV.data);
|
||||
byte *futureCells = reinterpret_cast<byte*>(SEGENV.data + dataSize);
|
||||
byte *cells = reinterpret_cast<byte*>(SEGENV.data);
|
||||
byte *futureCells = reinterpret_cast<byte*>(SEGENV.data + dataSize);
|
||||
uint16_t *gliderLength = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize * 2);
|
||||
uint16_t *oscillatorCRC = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize * 2 + sizeof(uint16_t));
|
||||
uint16_t *spaceshipCRC = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize * 2 + sizeof(uint16_t) * 2);
|
||||
bool *soloGlider = reinterpret_cast<bool*>(SEGENV.data + dataSize * 2 + sizeof(uint16_t) * 3);
|
||||
uint8_t *prevPalette = reinterpret_cast<uint8_t*>(SEGENV.data + dataSize * 2 + detectionSize);
|
||||
|
||||
uint16_t &generation = SEGENV.aux0; //Rename SEGENV/SEGMENT variables for readability
|
||||
uint16_t &generation = SEGENV.aux0; //Rename SEGENV/SEGMENT variables for readability
|
||||
bool allColors = SEGMENT.check1;
|
||||
bool overlayBG = SEGMENT.check2;
|
||||
bool wrap = SEGMENT.check3;
|
||||
byte blur = map2(SEGMENT.custom1, 0, 255, 255, 0);
|
||||
bool bgBlendMode = SEGMENT.custom1 > 220 && !overlayBG; // if blur is high and not overlaying, use bg blend mode
|
||||
byte bgBlur = map2(SEGMENT.custom1 - 220, 0, 35, 255, 128);
|
||||
byte blur = bgBlendMode ? map2(SEGMENT.custom1 - 220, 0, 35, 255, 128) : map2(SEGMENT.custom1, 0, 255, 255, 0);
|
||||
uint32_t bgColor = SEGCOLOR(1);
|
||||
uint32_t color = allColors ? random16() * random16() : SEGMENT.color_from_palette(0, false, PALETTE_SOLID_WRAP, 0);
|
||||
uint16_t cIndex;
|
||||
|
||||
if (SEGENV.call == 0) {
|
||||
SEGMENT.setUpLeds();
|
||||
@@ -5225,11 +5224,11 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
|
||||
random16_set_seed(strip.now>>2); //seed the random generator
|
||||
//Setup Grid
|
||||
memset(cells, 0, dataSize);
|
||||
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
|
||||
cIndex = y * cols + x;
|
||||
for (unsigned x = 0; x < cols; x++) for (unsigned y = 0; y < rows; y++) {
|
||||
if (random8(100) < 32) { // ~32% chance of being alive
|
||||
setBitValue(cells, cIndex, true);
|
||||
if (!overlayBG) SEGMENT.setPixelColorXY(x,y, bgColor); // Initial color set in redraw loop
|
||||
setBitValue(cells, y * cols + x, true);
|
||||
if (overlayBG) SEGMENT.setPixelColorXY(x,y, allColors ? random16() * random16() : SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0));
|
||||
else SEGMENT.setPixelColorXY(x,y, bgColor); // Initial color set in redraw loop
|
||||
}
|
||||
}
|
||||
memcpy(futureCells, cells, dataSize);
|
||||
@@ -5250,99 +5249,98 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
|
||||
*gliderLength = cols * rows / a * 4;
|
||||
}
|
||||
|
||||
int aliveCount = 0; // Solo glider detection
|
||||
bool blurDead = SEGENV.step > strip.now && blur && !bgBlendMode && !overlayBG;
|
||||
bool blurDead = SEGENV.step > strip.now && blur !=255 && !bgBlendMode && !overlayBG;
|
||||
bool palChanged = SEGMENT.palette != *prevPalette && !allColors;
|
||||
bool newGame = generation == 1;
|
||||
if (palChanged) *prevPalette = SEGMENT.palette;
|
||||
|
||||
// Redraw Loop
|
||||
// Redraw if paused (remove blur), palette changed, overlaying background (avoid flicker)
|
||||
// Always redraw dead cells if not overlaying background. Allows overlayFG by default.
|
||||
// Generation 1 draws alive cells randomly and fades dead cells
|
||||
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
|
||||
cIndex = y * cols + x;
|
||||
bool alive = getBitValue(cells, cIndex);
|
||||
if (alive) aliveCount++;
|
||||
uint32_t cellColor = SEGMENT.getPixelColorXY(x,y);
|
||||
bool aliveBgColor = (alive && !overlayBG && generation == 1 && cellColor == bgColor);
|
||||
if (blurDead || newGame || palChanged || overlayBG) {
|
||||
for (unsigned x = 0; x < cols; x++) for (unsigned y = 0; y < rows; y++) {
|
||||
unsigned cIndex = y * cols + x;
|
||||
uint32_t cellColor = SEGMENT.getPixelColorXY(x,y);
|
||||
bool alive = getBitValue(cells, cIndex);
|
||||
bool aliveBgColor = (!overlayBG && alive && newGame && cellColor == bgColor );
|
||||
|
||||
if ( alive && (palChanged || (aliveBgColor && !random(12)))) { // Palette change or spawn initial colors randomly
|
||||
uint32_t randomColor = allColors ? random16() * random16() : SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
|
||||
SEGMENT.setPixelColorXY(x,y, randomColor); // Recolor alive cells
|
||||
aliveBgColor = false;
|
||||
if ( alive && (palChanged || (aliveBgColor && !random(12)))) { // Palette change or spawn initial colors randomly
|
||||
uint32_t randomColor = allColors ? random16() * random16() : SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
|
||||
SEGMENT.setPixelColorXY(x,y, randomColor); // Recolor alive cells
|
||||
}
|
||||
else if ( alive && overlayBG && !aliveBgColor) SEGMENT.setPixelColorXY(x,y, cellColor); // Redraw alive cells for overlayBG
|
||||
if (!alive && palChanged && !overlayBG) SEGMENT.setPixelColorXY(x,y, bgColor); // Remove blurred cells from previous palette
|
||||
else if (!alive && blurDead) SEGMENT.setPixelColorXY(x,y, color_blend(cellColor, bgColor, blur));// Blur dead cells (paused)
|
||||
else if (!alive && !overlayBG && generation == 1) SEGMENT.setPixelColorXY(x,y, color_blend(cellColor, bgColor, 16)); // Fade dead cells on generation 1
|
||||
}
|
||||
else if ( alive && overlayBG) SEGMENT.setPixelColorXY(x,y, cellColor); // Redraw alive cells for overlayBG
|
||||
if (!alive && palChanged && !overlayBG) SEGMENT.setPixelColorXY(x,y, bgColor); // Remove blurred cells from previous palette
|
||||
else if (!alive && blurDead) SEGMENT.setPixelColorXY(x,y, color_blend(cellColor, bgColor, blur));// Blur dead cells (paused)
|
||||
else if ((!alive || aliveBgColor) && !overlayBG && !bgBlendMode) SEGMENT.setPixelColorXY(x,y, cellColor); // Redraw dead cells/alive off cells for default overlayFG
|
||||
else if (!alive && !overlayBG && generation == 1) SEGMENT.setPixelColorXY(x,y, color_blend(cellColor, bgColor, 16)); // Fade dead cells (bgBlendMode) on generation 1
|
||||
}
|
||||
|
||||
if (SEGENV.step > strip.now || strip.now - SEGENV.step < 1000 / (uint32_t)map(SEGMENT.speed,0,255,1,64)) return FRAMETIME; //skip if not enough time has passed (1-64 updates/sec)
|
||||
if (!SEGMENT.speed || SEGENV.step > strip.now || (SEGMENT.speed != 255 && strip.now - SEGENV.step < 1000 / map2(SEGMENT.speed,0,254,0,60))) return FRAMETIME; //(0 - 60) updates/sec 255 is uncapped
|
||||
|
||||
//Update Game of Life
|
||||
bool cellChanged = false; // Detect still live and dead grids
|
||||
//cell coordinates
|
||||
uint16_t cX;
|
||||
uint16_t cY;
|
||||
unsigned aliveCount = 0; // Detects dead grids and solo gliders
|
||||
bool disableWrap = !wrap || (generation % 1500 == 0 || *soloGlider); // Disable wrap every 1500 generations to prevent undetected repeats
|
||||
//Loop through all cells. Count neighbors, apply rules, setPixel
|
||||
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
|
||||
byte neighbors = 0;
|
||||
byte colorCount = 0; //track number of valid colors
|
||||
uint32_t nColors[3]; // track 3 colors, dying cells may overwrite but this wont be used
|
||||
for (unsigned x = 0; x < cols; x++) for (unsigned y = 0; y < rows; y++) {
|
||||
unsigned cIndex = y * cols + x;
|
||||
bool cellValue = getBitValue(cells, cIndex);
|
||||
uint32_t cellColor = SEGMENT.getPixelColorXY(x, y);
|
||||
if (cellValue) aliveCount++;
|
||||
|
||||
for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix
|
||||
if (i == 0 && j == 0) continue; // ignore itself
|
||||
if (!wrap || generation % 1500 == 0 || aliveCount == 5) { //no wrap, disable wrap every 1500 generations to prevent undetected repeats
|
||||
cX = x + i;
|
||||
cY = y + j;
|
||||
if (cX < 0 || cY < 0 || cX >= cols || cY >= rows) continue; //skip if out of bounds
|
||||
} else { //wrap around
|
||||
cX = (x + i + cols) % cols;
|
||||
cY = (y + j + rows) % rows;
|
||||
}
|
||||
cIndex = cY * cols + cX; //neighbor cell index
|
||||
// count neighbors and store upto 3 neighbor colors
|
||||
if (getBitValue(cells, cIndex)) { //if alive
|
||||
neighbors++;
|
||||
if (!getBitValue(futureCells, cIndex) || SEGMENT.getPixelColorXY(cX,cY) == bgColor) continue; //parent just died, color lost
|
||||
color = SEGMENT.getPixelColorXY(cX, cY);
|
||||
nColors[colorCount % 3] = color;
|
||||
colorCount++;
|
||||
unsigned neighbors = 0, colorCount = 0;
|
||||
unsigned neighborIndexes[3];
|
||||
|
||||
// Count neighbors and store indexes, get neighbor colors later if needed
|
||||
for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // Iterate through all neighbors
|
||||
if (i == 0 && j == 0) continue; // Ignore self
|
||||
if (i == 1 && j == 0 && !cellValue && !neighbors) break; // Cell can't be born with no neighbors and 2 remaining checks
|
||||
int nX = x + i;
|
||||
int nY = y + j;
|
||||
if (nX < 0) {if (disableWrap) continue; nX = cols - 1;}
|
||||
else if (nX >= cols) {if (disableWrap) continue; nX = 0;}
|
||||
if (nY < 0) {if (disableWrap) continue; nY = rows - 1;}
|
||||
else if (nY >= rows) {if (disableWrap) continue; nY = 0;}
|
||||
|
||||
unsigned nIndex = nY * cols + nX; // Neighbor cell index
|
||||
if (getBitValue(cells, nIndex)) {
|
||||
++neighbors;
|
||||
if (neighbors > 3) break; // Cell dies, stop neighbor loop
|
||||
neighborIndexes[neighbors - 1] = nIndex; // Store alive neighbor index
|
||||
}
|
||||
}
|
||||
|
||||
if (!cellValue && neighbors != 3 && cellColor == bgColor) continue; // Skip dead cells with no neighbors and no color
|
||||
|
||||
// Rules of Life
|
||||
cIndex = y * cols + x; //current cell index
|
||||
bool cellValue = getBitValue(cells, cIndex);
|
||||
if ((cellValue) && (neighbors < 2 || neighbors > 3)) {
|
||||
if (cellValue && (neighbors < 2 || neighbors > 3)) {
|
||||
// Loneliness or Overpopulation
|
||||
cellChanged = true;
|
||||
setBitValue(futureCells, cIndex, false);
|
||||
// Blur/turn off dying cells
|
||||
if (!overlayBG) SEGMENT.setPixelColorXY(x,y, color_blend(SEGMENT.getPixelColorXY(x,y), bgColor, bgBlendMode ? bgBlur : blur));
|
||||
if (!overlayBG) SEGMENT.setPixelColorXY(x,y, color_blend(cellColor, bgColor, blur));
|
||||
}
|
||||
else if (!(cellValue) && (neighbors == 3)) {
|
||||
else if (neighbors == 3 && !cellValue) {
|
||||
// Reproduction
|
||||
setBitValue(futureCells, cIndex, true);
|
||||
cellChanged = true;
|
||||
// find dominant color and assign it to a new born cell no longer storing colors, if parent dies the color is lost
|
||||
uint32_t dominantColor;
|
||||
if (colorCount == 3) { //All parents survived
|
||||
if ((nColors[0] == nColors[1]) || (nColors[0] == nColors[2])) dominantColor = nColors[0];
|
||||
else if (nColors[1] == nColors[2]) dominantColor = nColors[1];
|
||||
else dominantColor = nColors[random8(3)];
|
||||
// Get Colors
|
||||
uint32_t nColors[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
unsigned nIndex = neighborIndexes[i];
|
||||
if (!getBitValue(futureCells, nIndex)) continue; // Parent just died, color lost or blended
|
||||
uint32_t nColor = SEGMENT.getPixelColorXY(nIndex % cols, nIndex / cols);
|
||||
if (nColor == bgColor) continue;
|
||||
color = nColor; // Update last seen color
|
||||
nColors[colorCount++] = nColor;
|
||||
|
||||
}
|
||||
else if (colorCount == 2) dominantColor = nColors[random8(2)]; // 1 leading parent died
|
||||
else if (colorCount == 1) dominantColor = nColors[0]; // 2 leading parents died
|
||||
else dominantColor = color; // all parents died use last seen color
|
||||
// mutate color chance
|
||||
if (random8() < SEGMENT.intensity || dominantColor == bgColor) dominantColor = allColors ? random16() * random16() : SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
|
||||
SEGMENT.setPixelColorXY(x,y, dominantColor);
|
||||
setBitValue(futureCells, cIndex, true);
|
||||
uint32_t birthColor = colorCount ? nColors[random8(colorCount)] : color; // Uses last seen color if no surviving neighbors
|
||||
// Mutate color chance
|
||||
if (random8() < SEGMENT.intensity) birthColor = allColors ? random16() * random16() : SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
|
||||
SEGMENT.setPixelColorXY(x,y, birthColor);
|
||||
}
|
||||
else { // blur dead cells
|
||||
if (!cellValue && !overlayBG && !bgBlendMode) SEGMENT.setPixelColorXY(x,y, color_blend(SEGMENT.getPixelColorXY(x,y), bgColor, blur));
|
||||
else { // Blur dead cells and redraw alive cells
|
||||
if (cellValue) SEGMENT.setPixelColorXY(x, y, cellColor == bgColor ? color : cellColor); // Redraw alive, fixes fading cells
|
||||
else if (blur != 255 && !overlayBG && !bgBlendMode) SEGMENT.setPixelColorXY(x, y, color_blend(cellColor, bgColor, blur));
|
||||
}
|
||||
|
||||
}
|
||||
//update cell values
|
||||
memcpy(cells, futureCells, dataSize);
|
||||
@@ -5351,7 +5349,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
|
||||
uint16_t crc = crc16((const unsigned char*)cells, dataSize);
|
||||
|
||||
bool repetition = false;
|
||||
if (!cellChanged || crc == *oscillatorCRC || crc == *spaceshipCRC) repetition = true; //check if cell changed this gen and compare previous stored crc values
|
||||
if (!aliveCount || crc == *oscillatorCRC || crc == *spaceshipCRC) repetition = true; //check if cell changed this gen and compare previous stored crc values
|
||||
if (repetition) {
|
||||
generation = 0; // reset on next call
|
||||
SEGENV.step += 1000; // pause final generation for 1 second
|
||||
@@ -5360,6 +5358,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
|
||||
// Update CRC values
|
||||
if (generation % 16 == 0) *oscillatorCRC = crc;
|
||||
if (*gliderLength && generation % *gliderLength == 0) *spaceshipCRC = crc;
|
||||
if (aliveCount == 5) *soloGlider = true; else *soloGlider = false;
|
||||
|
||||
generation++;
|
||||
SEGENV.step = strip.now;
|
||||
|
||||
Reference in New Issue
Block a user