RGBMatrixDisplay/src/mode/GameOfLife/GameOfLife.h

153 lines
3.7 KiB
C++

#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