153 lines
3.7 KiB
C++
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
|