mirror of
https://github.com/wled/WLED.git
synced 2026-04-20 06:04:29 +00:00
Add flashing effect on line clear in TetrisAI_v2. (#5320)
* Add flashing effect on line clear in TetrisAI_v2. See https://imgur.com/1dsNCVd for a demo. * Address feedback for tetris flashing effect. * Address width == 32 case for isLineFull
This commit is contained in:
@@ -5,9 +5,12 @@
|
||||
#include "tetrisaigame.h"
|
||||
// By: muebau
|
||||
|
||||
bool noFlashOnClear = false;
|
||||
|
||||
typedef struct TetrisAI_data
|
||||
{
|
||||
unsigned long lastTime = 0;
|
||||
unsigned long clearingStartTime = 0;
|
||||
TetrisAIGame tetris;
|
||||
uint8_t intelligence;
|
||||
uint8_t rotate;
|
||||
@@ -31,16 +34,27 @@ void drawGrid(TetrisAIGame* tetris, TetrisAI_data* tetrisai_data)
|
||||
//GRID
|
||||
for (auto index_y = 4; index_y < tetris->grid.height; index_y++)
|
||||
{
|
||||
bool isRowClearing = tetris->grid.gridBW.clearingRows[index_y];
|
||||
for (auto index_x = 0; index_x < tetris->grid.width; index_x++)
|
||||
{
|
||||
CRGB color;
|
||||
if (*tetris->grid.getPixel(index_x, index_y) == 0)
|
||||
{
|
||||
uint8_t gridPixel = *tetris->grid.getPixel(index_x, index_y);
|
||||
if (isRowClearing) {
|
||||
if (noFlashOnClear) {
|
||||
color = CRGB::Gray;
|
||||
} else {
|
||||
//flash color white and black every 200ms
|
||||
color = (strip.now % 200) < 150
|
||||
? CRGB::Gray
|
||||
: CRGB::Black;
|
||||
}
|
||||
}
|
||||
else if (gridPixel == 0) {
|
||||
//BG color
|
||||
color = SEGCOLOR(1);
|
||||
}
|
||||
//game over animation
|
||||
else if(*tetris->grid.getPixel(index_x, index_y) == 254)
|
||||
else if (gridPixel == 254)
|
||||
{
|
||||
//use fg
|
||||
color = SEGCOLOR(0);
|
||||
@@ -48,7 +62,7 @@ void drawGrid(TetrisAIGame* tetris, TetrisAI_data* tetrisai_data)
|
||||
else
|
||||
{
|
||||
//spread the color over the whole palette
|
||||
uint8_t colorIndex = *tetris->grid.getPixel(index_x, index_y) * 32;
|
||||
uint8_t colorIndex = gridPixel * 32;
|
||||
colorIndex += tetrisai_data->colorOffset;
|
||||
color = ColorFromPalette(SEGPALETTE, colorIndex, 255, NOBLEND);
|
||||
}
|
||||
@@ -170,6 +184,7 @@ void mode_2DTetrisAI()
|
||||
|
||||
tetrisai_data->tetris = TetrisAIGame(gridWidth, gridHeight, nLookAhead, piecesData, numPieces);
|
||||
tetrisai_data->tetris.state = TetrisAIGame::States::INIT;
|
||||
tetrisai_data->clearingStartTime = 0;
|
||||
SEGMENT.fill(SEGCOLOR(1));
|
||||
}
|
||||
|
||||
@@ -184,7 +199,21 @@ void mode_2DTetrisAI()
|
||||
tetrisai_data->tetris.ai.bumpiness = -0.184483f + dui;
|
||||
}
|
||||
|
||||
if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_MOVE)
|
||||
//end line clearing flashing effect if needed
|
||||
if (tetrisai_data->tetris.grid.gridBW.hasClearingRows())
|
||||
{
|
||||
if (tetrisai_data->clearingStartTime == 0) {
|
||||
tetrisai_data->clearingStartTime = strip.now;
|
||||
}
|
||||
if (strip.now - tetrisai_data->clearingStartTime > 750)
|
||||
{
|
||||
tetrisai_data->tetris.grid.gridBW.clearedLinesReadyForRemoval = true;
|
||||
tetrisai_data->tetris.grid.cleanupFullLines();
|
||||
tetrisai_data->clearingStartTime = 0;
|
||||
}
|
||||
drawGrid(&tetrisai_data->tetris, tetrisai_data);
|
||||
}
|
||||
else if (tetrisai_data->tetris.state == TetrisAIGame::ANIMATE_MOVE)
|
||||
{
|
||||
|
||||
if (strip.now - tetrisai_data->lastTime > msDelayMove)
|
||||
@@ -229,6 +258,7 @@ class TetrisAIUsermod : public Usermod
|
||||
{
|
||||
|
||||
private:
|
||||
static const char _name[];
|
||||
|
||||
public:
|
||||
void setup()
|
||||
@@ -236,6 +266,20 @@ public:
|
||||
strip.addEffect(255, &mode_2DTetrisAI, _data_FX_MODE_2DTETRISAI);
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root) override
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top["noFlashOnClear"] = noFlashOnClear;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root) override
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
bool configComplete = !top.isNull();
|
||||
configComplete &= getJsonValue(top["noFlashOnClear"], noFlashOnClear);
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
@@ -247,6 +291,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
const char TetrisAIUsermod::_name[] PROGMEM = "TetrisAI_v2";
|
||||
|
||||
static TetrisAIUsermod tetrisai_v2;
|
||||
REGISTER_USERMOD(tetrisai_v2);
|
||||
@@ -25,11 +25,18 @@ public:
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
std::vector<uint32_t> pixels;
|
||||
// When a row fills, we mark it here first so it can flash before being
|
||||
// fully removed.
|
||||
std::vector<bool> clearingRows;
|
||||
// True when a line clearing flashing effect is over and we're ready to
|
||||
// fully clean up the lines
|
||||
bool clearedLinesReadyForRemoval = false;
|
||||
|
||||
GridBW(uint8_t width, uint8_t height):
|
||||
width(width),
|
||||
height(height),
|
||||
pixels(height)
|
||||
pixels(height),
|
||||
clearingRows(height)
|
||||
{
|
||||
if (width > 32)
|
||||
{
|
||||
@@ -84,9 +91,26 @@ public:
|
||||
piece->landingY = piece->landingY > 0 ? piece->landingY - 1 : 0;
|
||||
}
|
||||
|
||||
bool hasClearingRows()
|
||||
{
|
||||
for (bool rowClearing : clearingRows)
|
||||
{
|
||||
if (rowClearing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cleanupFullLines()
|
||||
{
|
||||
// Skip cleanup if there are rows clearing
|
||||
if (hasClearingRows() && !clearedLinesReadyForRemoval) {
|
||||
return;
|
||||
}
|
||||
uint8_t offset = 0;
|
||||
bool doneRemovingClearedLines = false;
|
||||
|
||||
//from "height - 1" to "0", so from bottom row to top
|
||||
for (uint8_t row = height; row-- > 0; )
|
||||
@@ -94,8 +118,13 @@ public:
|
||||
//full line?
|
||||
if (isLineFull(row))
|
||||
{
|
||||
offset++;
|
||||
pixels[row] = 0x0;
|
||||
if (clearedLinesReadyForRemoval) {
|
||||
offset++;
|
||||
pixels[row] = 0x0;
|
||||
doneRemovingClearedLines = true;
|
||||
} else {
|
||||
clearingRows[row] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -105,11 +134,20 @@ public:
|
||||
pixels[row] = 0x0;
|
||||
}
|
||||
}
|
||||
if (doneRemovingClearedLines) {
|
||||
clearingRows.assign(height, false);
|
||||
clearedLinesReadyForRemoval = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isLineFull(uint8_t y)
|
||||
{
|
||||
return pixels[y] == (uint32_t)((1 << width) - 1);
|
||||
return pixels[y] == (width >= 32 ? UINT32_MAX : (1U << width) - 1);
|
||||
}
|
||||
|
||||
bool isLineReadyForRemoval(uint8_t y)
|
||||
{
|
||||
return clearedLinesReadyForRemoval && isLineFull(y);
|
||||
}
|
||||
|
||||
void reset()
|
||||
@@ -121,6 +159,8 @@ public:
|
||||
|
||||
pixels.clear();
|
||||
pixels.resize(height);
|
||||
clearingRows.assign(height, false);
|
||||
clearedLinesReadyForRemoval = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
//from "height - 1" to "0", so from bottom row to top
|
||||
for (uint8_t y = height; y-- > 0; )
|
||||
{
|
||||
if (gridBW.isLineFull(y))
|
||||
if (gridBW.isLineReadyForRemoval(y))
|
||||
{
|
||||
offset++;
|
||||
for (uint8_t x = 0; x < width; x++)
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
This usermod adds a self-playing Tetris game as an 'effect'. The mod requires version 0.14 or higher as it relies on matrix support. The effect was tested on an ESP32 4MB with a WS2812B 16x16 matrix.
|
||||
|
||||
Version 1.0
|
||||
PHOTOSENSITIVE EPILEPSY WARNING: By default the effect features a flashing animation on line clear. This can be disabled
|
||||
from the usermod settings page in WLED.
|
||||
|
||||
## Installation
|
||||
|
||||
Just activate the usermod with `-D USERMOD_TETRISAI` and the effect will become available under the name 'Tetris AI'. If you are running out of flash memory, use a different memory layout (e.g. [WLED_ESP32_4MB_256KB_FS.csv](https://github.com/wled-dev/WLED/blob/main/tools/WLED_ESP32_4MB_256KB_FS.csv)).
|
||||
To activate the usermod, add the following line to your platformio_override.ini
|
||||
`custom_usermods = tetrisai_v2`
|
||||
The effect will then become available under the name 'Tetris AI'. If you are running out of flash memory, use a different memory layout (e.g. [WLED_ESP32_4MB_256KB_FS.csv](https://github.com/wled-dev/WLED/blob/main/tools/WLED_ESP32_4MB_256KB_FS.csv)).
|
||||
|
||||
If needed simply add to `platformio_override.ini` (or `platformio_override.ini`):
|
||||
If needed simply add to `platformio_override.ini`:
|
||||
|
||||
```ini
|
||||
board_build.partitions = tools/WLED_ESP32_4MB_256KB_FS.csv
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
}
|
||||
|
||||
//line full if all ones in mask :-)
|
||||
if (grid.isLineFull(row))
|
||||
if (grid.isLineReadyForRemoval(row))
|
||||
{
|
||||
rating->fullLines++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user