199 lines
4.8 KiB
C++
199 lines
4.8 KiB
C++
#ifndef GAMEOFLIFE_H
|
|
#define GAMEOFLIFE_H
|
|
|
|
#include "mode/Mode.h"
|
|
#include "display/Display.h"
|
|
#include "Cell.h"
|
|
|
|
enum ColorMode {
|
|
BLACK_WHITE, GRAYSCALE, COLOR_FADE, RANDOM_COLOR
|
|
};
|
|
|
|
class GameOfLife : public Mode<GameOfLife> {
|
|
|
|
private:
|
|
|
|
ColorMode colorMode;
|
|
|
|
microseconds_t runtime = 0;
|
|
|
|
uint8_t steps = 0;
|
|
|
|
uint16_t aliveCount = 0;
|
|
|
|
uint16_t lastAliveCount = 0;
|
|
|
|
uint8_t isSteadyCount = 0;
|
|
|
|
Cell *cells;
|
|
|
|
Cell *next;
|
|
|
|
public:
|
|
|
|
explicit GameOfLife(Display *display, ColorMode colorMode) :
|
|
Mode(display),
|
|
colorMode(colorMode) {
|
|
cells = (Cell *) malloc(display->pixelCount * sizeof(Cell));
|
|
for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) {
|
|
kill(cell);
|
|
}
|
|
next = (Cell *) malloc(display->pixelCount * sizeof(Cell));
|
|
for (Cell *cell = next; cell < next + display->pixelCount; cell++) {
|
|
kill(cell);
|
|
}
|
|
}
|
|
|
|
~GameOfLife() override {
|
|
if (cells != nullptr) {
|
|
free(cells);
|
|
cells = nullptr;
|
|
}
|
|
if (next != nullptr) {
|
|
free(next);
|
|
next = nullptr;
|
|
}
|
|
}
|
|
|
|
const char *getName() override {
|
|
switch (colorMode) {
|
|
case BLACK_WHITE:
|
|
return "Game of Life (black white)";
|
|
case GRAYSCALE:
|
|
return "Game of Life (grayscale)";
|
|
case COLOR_FADE:
|
|
return "Game of Life (color fade)";
|
|
case RANDOM_COLOR:
|
|
return "Game of Life (random color)";
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
void doStep(microseconds_t dt) override {
|
|
runtime += dt;
|
|
if (runtime >= 500000) {
|
|
runtime = 0;
|
|
if (lastAliveCount == aliveCount) {
|
|
isSteadyCount++;
|
|
} else {
|
|
isSteadyCount = 0;
|
|
}
|
|
lastAliveCount = aliveCount;
|
|
if (steps++ == 0 || aliveCount == 0 || isSteadyCount >= 15) {
|
|
randomFill();
|
|
} else {
|
|
nextGeneration();
|
|
}
|
|
}
|
|
animate(dt);
|
|
}
|
|
|
|
private:
|
|
|
|
void randomFill() {
|
|
isSteadyCount = 0;
|
|
for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) {
|
|
if (random(4) == 0) {
|
|
if (!cell->alive) {
|
|
spawn(cell);
|
|
}
|
|
} else {
|
|
if (cell->alive) {
|
|
kill(cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void spawn(Cell *cell) {
|
|
cell->color = randomColor();
|
|
cell->alive = true;
|
|
aliveCount++;
|
|
}
|
|
|
|
void kill(Cell *cell) {
|
|
cell->alive = false;
|
|
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->alive) {
|
|
if (around <= 2 || 6 <= around) {
|
|
kill(dst);
|
|
}
|
|
} else if (around == 3) {
|
|
spawn(dst);
|
|
}
|
|
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->alive ? "x|" : " |");
|
|
cell++;
|
|
}
|
|
Serial.println();
|
|
Serial.println();
|
|
}
|
|
}
|
|
|
|
void animate(microseconds_t dt) {
|
|
Cell *cell = cells;
|
|
for (int y = 0; y < display->height; y++) {
|
|
for (int x = 0; x < display->width; x++) {
|
|
cell->animate(dt);
|
|
uint8_t brightness;
|
|
switch (colorMode) {
|
|
case BLACK_WHITE:
|
|
brightness = cell->alive ? 255 : 0;
|
|
display->set(x, y, gray(brightness));
|
|
break;
|
|
case GRAYSCALE:
|
|
brightness = (uint8_t) cell->fade;
|
|
display->set(x, y, gray(brightness));
|
|
break;
|
|
case COLOR_FADE:
|
|
display->set(x, y, {cell->getR(), cell->getG(), cell->getB()});
|
|
break;
|
|
case RANDOM_COLOR:
|
|
display->set(x, y, cell->alive ? cell->color : BLACK);
|
|
break;
|
|
}
|
|
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)->alive ? 1 : 0;
|
|
}
|
|
|
|
Cell *get(int x, int y) {
|
|
return cells + display->width * y + x;
|
|
}
|
|
};
|
|
|
|
#endif
|