#ifndef GAMEOFLIFE_H #define GAMEOFLIFE_H #include "mode/Mode.h" #include "display/Display.h" #include "Cell.h" class GameOfLife : public Mode { private: millis_t runtime = 0; uint8_t steps = 0; uint16_t aliveCount = 0; uint16_t lastAliveCount = 0; Cell *cells; Cell *last; Cell *next; public: explicit GameOfLife(Display *display) : Mode(display, "Game of Life") { cells = (Cell *) malloc(display->pixelCount * sizeof(Cell)); last = cells; for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) { cell->state = DEAD; cell->value = 0; } next = (Cell *) malloc(display->pixelCount * sizeof(Cell)); for (Cell *cell = next; cell < next + display->pixelCount; cell++) { cell->state = DEAD; cell->value = 0; } } ~GameOfLife() override { if (cells != nullptr) { free(cells); cells = nullptr; } if (next != nullptr) { free(next); next = nullptr; } } void step(millis_t dt) override { runtime += dt; if (runtime >= 500) { runtime = 0; bool isSteady = lastAliveCount == aliveCount; lastAliveCount = aliveCount; if (steps++ == 0 || aliveCount == 0 || isSteady) { randomFill(); } else { nextGeneration(); } } animate(dt); } private: void randomFill() { for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) { if (random(4) == 0) { if (!cell->isAlive()) { cell->spawn(); aliveCount++; } } else { if (cell->isAlive()) { cell->kill(); aliveCount--; } } } } void nextGeneration() { memcpy(next, cells, display->pixelCount * sizeof(Cell)); Cell *src = cells; Cell *dst = next; for (int y = 0; y < display->height; y++) { for (int x = 0; x < display->width; x++) { uint8_t around = countAround(x, y); if (src->isAlive()) { if (around <= 2 || 4 <= around) { dst->kill(); aliveCount--; } } else if (around == 3) { dst->spawn(); aliveCount++; } src++; dst++; } } memcpy(cells, next, display->pixelCount * sizeof(Cell)); } void print() { Cell *cell = cells; for (int y = 0; y < display->height; y++) { Serial.print("|"); for (int x = 0; x < display->width; x++) { Serial.print(cell->isAlive() ? "x|" : " |"); cell++; } Serial.println(); Serial.println(); } } void animate(millis_t dt) { Cell *cell = cells; for (int y = 0; y < display->height; y++) { for (int x = 0; x < display->width; x++) { cell->animate(dt); int xx = (y % 2) == 0 ? display->width - x - 1 : x; display->set(xx, y, cell->getR(), cell->getG(), cell->getB()); cell++; } } } uint8_t countAround(int x, int y) { return countIfAlive(x - 1, y - 1) + countIfAlive(x + 0, y - 1) + countIfAlive(x + 1, y - 1) + countIfAlive(x - 1, y + 0) + /* */ countIfAlive(x + 1, y + 0) + countIfAlive(x - 1, y + 1) + countIfAlive(x + 0, y + 1) + countIfAlive(x + 1, y + 1); } uint8_t countIfAlive(int x, int y) { if (x < 0 || y < 0 || x >= display->width || y >= display->height) { return 0; } return get(x, y)->isAlive() ? 1 : 0; } Cell *get(int x, int y) { return cells + display->width * y + x; } }; #endif