From 14fefed0d31e808addd19823cf892a655cbb79e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Sun, 26 Dec 2021 13:12:03 +0100 Subject: [PATCH] GameOfLife working (black white, grayscale, color fade, random color) --- src/display/Display.h | 8 +++ src/main.cpp | 34 ++++++++--- src/mode/GameOfLife/Cell.h | 88 ++++++++++++----------------- src/mode/GameOfLife/GameOfLife.h | 97 ++++++++++++++++++++++++-------- src/mode/Mode.h | 11 +--- src/mode/Pong/Pong.h | 6 +- src/mode/Test/Border.h | 6 +- 7 files changed, 156 insertions(+), 94 deletions(-) diff --git a/src/display/Display.h b/src/display/Display.h index 38c7dbd..d05e99c 100644 --- a/src/display/Display.h +++ b/src/display/Display.h @@ -33,6 +33,14 @@ public: clear(); } + void set(uint8_t x, uint8_t y, uint32_t color) { + if (x >= width || y >= height) { + Serial.printf("ERROR: Cannot set pixel (%d/%d) in matrix (%d/%d).\n", x, y, width, height); + return; + } + leds.setPixelColor(y * width + x, color); + } + void set(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b) { if (x >= width || y >= height) { Serial.printf("ERROR: Cannot set pixel (%d/%d) in matrix (%d/%d).\n", x, y, width, height); diff --git a/src/main.cpp b/src/main.cpp index 831f177..ac29cfd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,12 +5,18 @@ #include "mode/Test/Border.h" enum ModeId { - NONE, BORDER, GAME_OF_LIFE, PONG + NONE, + BORDER, + GAME_OF_LIFE_BLACK_WHITE, + GAME_OF_LIFE_WHITE_FADE, + GAME_OF_LIFE_COLOR_FADE, + GAME_OF_LIFE_RANDOM_COLOR, + PONG }; Display display(32, 8); -ModeId newModeId = GAME_OF_LIFE; +ModeId newModeId = GAME_OF_LIFE_COLOR_FADE; ModeId currentModeId = NONE; @@ -31,7 +37,7 @@ void setBrightness(int value); void setup() { delay(500); Serial.begin(115200); - Serial.println("\n\n\nStartup!\n"); + Serial.println("\n\n\nStartup!"); display.setup(); } @@ -47,6 +53,12 @@ void loop() { case '1': case '2': case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': newModeId = (ModeId) (input - '0'); break; case '+': @@ -96,21 +108,29 @@ void unloadOldMode() { void loadNewMode() { currentModeId = newModeId; lastMillis = 0; - Serial.printf("Loading mode: #%d\n", currentModeId); switch (currentModeId) { case NONE: break; case BORDER: mode = new Border(&display); break; - case GAME_OF_LIFE: - mode = new GameOfLife(&display); + case GAME_OF_LIFE_BLACK_WHITE: + mode = new GameOfLife(&display, BLACK_WHITE); + break; + case GAME_OF_LIFE_WHITE_FADE: + mode = new GameOfLife(&display, GRAYSCALE); + break; + case GAME_OF_LIFE_COLOR_FADE: + mode = new GameOfLife(&display, COLOR_FADE); + break; + case GAME_OF_LIFE_RANDOM_COLOR: + mode = new GameOfLife(&display, RANDOM_COLOR); break; case PONG: mode = new Pong(&display); break; } - Serial.printf("Loaded mode: %s\n\n", mode == nullptr ? "None" : mode->getName()); + Serial.printf("Mode: %s\n", mode == nullptr ? "None" : mode->getName()); } void stepMode() { diff --git a/src/mode/GameOfLife/Cell.h b/src/mode/GameOfLife/Cell.h index 02cf011..cee8f6a 100644 --- a/src/mode/GameOfLife/Cell.h +++ b/src/mode/GameOfLife/Cell.h @@ -3,78 +3,62 @@ #include "BASICS.h" -#define DEAD 0 -#define GROWING 1 -#define ALIVE 2 -#define DYING 3 - class Cell { public: - uint8_t state = DEAD; - uint8_t value = 0; + bool alive = false; - bool isAlive() const { - return state == GROWING || state == ALIVE; - } + double fade = 0.0; - void spawn() { - state = GROWING; - value = 0; - } + uint32_t color = 0; - void kill() { - state = DYING; - value = 255; - } - - void animate(uint8_t dt) { - switch (state) { - case GROWING: - if (dt < 255 - value) { - value += dt; - } else { - value = 255; - state = ALIVE; - } - break; - case DYING: - if (dt < value) { - value -= dt; - } else { - value = 0; - state = DEAD; - } - break; + void animate(double per255Points) { + if (alive) { + fade = min(255.0, fade + per255Points); + } else { + fade = max(0.0, fade - per255Points); } } uint8_t getR() const { - if (state == ALIVE) { - return 255; + if (alive) { + if (fade < 128) { + return 0; + } else { + return (uint8_t) ((fade - 128) * 2.0 + 1); + } + } else { + if (fade < 128) { + return (uint8_t) (fade * 2.0 + 1); + } else { + return 255; + } } - if (state == DYING) { - return value; - } - return 0; } uint8_t getG() const { - if (state == ALIVE) { - return 255; + if (alive) { + if (fade < 128) { + return (uint8_t) (fade * 2.0 + 1); + } else { + return 255; + } + } else { + if (fade < 128) { + return 0; + } else { + return (uint8_t) ((fade - 128) * 2.0 + 1); + } } - if (state == GROWING) { - return value; - } - return 0; } uint8_t getB() const { - if (state == ALIVE) { - return 255; + if (fade < 128) { + return 0; + } else { + return (uint8_t) ((fade - 128) * 2.0 + 1); } - return 0; } }; diff --git a/src/mode/GameOfLife/GameOfLife.h b/src/mode/GameOfLife/GameOfLife.h index ee50f2e..f731a04 100644 --- a/src/mode/GameOfLife/GameOfLife.h +++ b/src/mode/GameOfLife/GameOfLife.h @@ -5,10 +5,16 @@ #include "display/Display.h" #include "Cell.h" +enum ColorMode { + BLACK_WHITE, GRAYSCALE, COLOR_FADE, RANDOM_COLOR +}; + class GameOfLife : public Mode { private: + ColorMode colorMode; + millis_t runtime = 0; uint8_t steps = 0; @@ -17,25 +23,24 @@ private: uint16_t lastAliveCount = 0; + uint8_t isSteadyCount = 0; + Cell *cells; - Cell *last; Cell *next; public: - explicit GameOfLife(Display *display) : - Mode(display, "Game of Life") { + explicit GameOfLife(Display *display, ColorMode colorMode) : + Mode(display), + colorMode(colorMode) { cells = (Cell *) malloc(display->pixelCount * sizeof(Cell)); - last = cells; for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) { - cell->state = DEAD; - cell->value = 0; + kill(cell); } next = (Cell *) malloc(display->pixelCount * sizeof(Cell)); for (Cell *cell = next; cell < next + display->pixelCount; cell++) { - cell->state = DEAD; - cell->value = 0; + kill(cell); } } @@ -50,13 +55,31 @@ public: } } + 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 step(millis_t dt) override { runtime += dt; if (runtime >= 500) { runtime = 0; - bool isSteady = lastAliveCount == aliveCount; + if (lastAliveCount == aliveCount) { + isSteadyCount++; + } else { + isSteadyCount = 0; + } lastAliveCount = aliveCount; - if (steps++ == 0 || aliveCount == 0 || isSteady) { + if (steps++ == 0 || aliveCount == 0 || isSteadyCount >= 15) { randomFill(); } else { nextGeneration(); @@ -68,21 +91,31 @@ public: private: void randomFill() { + isSteadyCount = 0; for (Cell *cell = cells; cell < cells + display->pixelCount; cell++) { if (random(4) == 0) { - if (!cell->isAlive()) { - cell->spawn(); - aliveCount++; + if (!cell->alive) { + spawn(cell); } } else { - if (cell->isAlive()) { - cell->kill(); - aliveCount--; + if (cell->alive) { + kill(cell); } } } } + void spawn(Cell *cell) { + cell->color = random(16777216); + cell->alive = true; + aliveCount++; + } + + void kill(Cell *cell) { + cell->alive = false; + aliveCount--; + } + void nextGeneration() { memcpy(next, cells, display->pixelCount * sizeof(Cell)); Cell *src = cells; @@ -90,14 +123,12 @@ private: 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--; + if (src->alive) { + if (around <= 2 || 6 <= around) { + kill(dst); } } else if (around == 3) { - dst->spawn(); - aliveCount++; + spawn(dst); } src++; dst++; @@ -111,7 +142,7 @@ private: for (int y = 0; y < display->height; y++) { Serial.print("|"); for (int x = 0; x < display->width; x++) { - Serial.print(cell->isAlive() ? "x|" : " |"); + Serial.print(cell->alive ? "x|" : " |"); cell++; } Serial.println(); @@ -125,7 +156,23 @@ private: 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()); + uint8_t brightness; + switch (colorMode) { + case BLACK_WHITE: + brightness = cell->alive ? 255 : 0; + display->set(xx, y, brightness, brightness, brightness); + break; + case GRAYSCALE: + brightness = (uint8_t) cell->fade; + display->set(xx, y, brightness, brightness, brightness); + break; + case COLOR_FADE: + display->set(xx, y, cell->getR(), cell->getG(), cell->getB()); + break; + case RANDOM_COLOR: + display->set(xx, y, cell->alive ? cell->color : 0); + break; + } cell++; } } @@ -141,7 +188,7 @@ private: if (x < 0 || y < 0 || x >= display->width || y >= display->height) { return 0; } - return get(x, y)->isAlive() ? 1 : 0; + return get(x, y)->alive ? 1 : 0; } Cell *get(int x, int y) { diff --git a/src/mode/Mode.h b/src/mode/Mode.h index 8206239..be9c1a3 100644 --- a/src/mode/Mode.h +++ b/src/mode/Mode.h @@ -10,21 +10,16 @@ protected: Display *display; - const char *name; - public: - explicit Mode(Display *display, const char *name) : - display(display), - name(name) { + explicit Mode(Display *display) : + display(display) { // nothing } virtual ~Mode() = default; - const char *getName() { - return name; - } + virtual const char *getName() = 0; virtual void step(millis_t dt) = 0; }; diff --git a/src/mode/Pong/Pong.h b/src/mode/Pong/Pong.h index c5d9bea..ca24ca8 100644 --- a/src/mode/Pong/Pong.h +++ b/src/mode/Pong/Pong.h @@ -20,7 +20,7 @@ private: public: explicit Pong(Display *display) : - Mode(display, "Pong"), + Mode(display), position(display->width / 2.0, display->height / 2.0), velocity(random(360), exp10(1)) { // nothing @@ -32,6 +32,10 @@ public: ~Pong() override = default; + const char *getName() override { + return "Pong"; + } + void step(millis_t dt) override { } diff --git a/src/mode/Test/Border.h b/src/mode/Test/Border.h index 1a3358c..b4a942c 100644 --- a/src/mode/Test/Border.h +++ b/src/mode/Test/Border.h @@ -8,12 +8,16 @@ class Border : public Mode { public: explicit Border(Display *display) : - Mode(display, "Border") { + Mode(display) { // nothing } ~Border() override = default; + const char *getName() override { + return "Border"; + } + void step(millis_t dt) override { for (int y = 0; y < display->height; y++) { for (int x = 0; x < display->width; x++) {