FX: GameOfLife

- better glider detection
- correct behaviour during transition
- optimisations
This commit is contained in:
Blaz Kristan 2023-01-16 18:53:52 +01:00
parent 63d8a902d5
commit 4147d6c67e

View File

@ -4844,17 +4844,18 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled
const uint16_t crcBufferLen = (SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
if (!SEGENV.allocateData(dataSize + sizeof(unsigned long))) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed
CRGB *prevLeds = reinterpret_cast<CRGB*>(SEGENV.data); CRGB *prevLeds = reinterpret_cast<CRGB*>(SEGENV.data);
unsigned long *resetMillis = reinterpret_cast<unsigned long*>(SEGENV.data + dataSize); // triggers reset uint16_t *crcBuffer = reinterpret_cast<uint16_t*>(SEGENV.data + dataSize);
CRGB backgroundColor = SEGCOLOR(1); CRGB backgroundColor = SEGCOLOR(1);
if (SEGENV.call == 0 || strip.now - *resetMillis > 5000) { if (SEGENV.call == 0 || strip.now - SEGMENT.step > 5000) {
*resetMillis = strip.now; SEGENV.step = strip.now;
SEGENV.aux0 = 0;
random16_set_seed(strip.now); //seed the random generator random16_set_seed(millis()>>2); //seed the random generator
//give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
@ -4862,17 +4863,18 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
if (state == 0) if (state == 0)
SEGMENT.setPixelColorXY(x,y, backgroundColor); SEGMENT.setPixelColorXY(x,y, backgroundColor);
else else
SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0)); SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255));
} }
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black;
memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen);
} else if (strip.now - SEGENV.step < FRAMETIME_FIXED * map(SEGMENT.speed,0,255,64,4)) {
SEGENV.aux1 = 0; // update only when appropriate time passes (in 42 FPS slots)
SEGENV.aux0 = 0xFFFF; return FRAMETIME;
} }
//copy previous leds (save previous generation) //copy previous leds (save previous generation)
//NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y); for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y);
//calculate new leds //calculate new leds
@ -4910,7 +4912,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
else if ((col == bgc) && (neighbors == 3)) { // Reproduction else if ((col == bgc) && (neighbors == 3)) { // Reproduction
//find dominantcolor and assign to cell //find dominant color and assign to cell
colorCount dominantColorCount = {backgroundColor, 0}; colorCount dominantColorCount = {backgroundColor, 0};
for (int i=0; i<9 && colorsCount[i].count != 0; i++) for (int i=0; i<9 && colorsCount[i].count != 0; i++)
if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
@ -4920,16 +4922,17 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
} //x,y } //x,y
// calculate CRC16 of leds // calculate CRC16 of leds
uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize-1); //ewowi: prevLeds instead of leds work as well, tbd: compare more patterns, see SR! uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize);
// check if we had same CRC and reset if needed // check if we had same CRC and reset if needed
bool repetition = false;
for (int i=0; i<crcBufferLen && !repetition; i++) repetition = (crc == crcBuffer[i]); // (Ewowi)
// same CRC would mean image did not change or was repeating itself // same CRC would mean image did not change or was repeating itself
if (!(crc == SEGENV.aux0 || crc == SEGENV.aux1)) *resetMillis = strip.now; //if no repetition avoid reset if (!repetition) SEGENV.step = strip.now; //if no repetition avoid reset
// remember last two // remember CRCs across frames
SEGENV.aux1 = SEGENV.aux0; crcBuffer[SEGENV.aux0] = crc;
SEGENV.aux0 = crc; ++SEGENV.aux0 %= crcBufferLen;
return FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots) return FRAMETIME;
} // mode_2Dgameoflife() } // mode_2Dgameoflife()
static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2"; static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2";