Skip to content

Conversation

Brandon502
Copy link
Contributor

@Brandon502 Brandon502 commented Oct 9, 2025

A lightweight version of my MM version to keep code size differences as small as possible (adds ~120 Bytes). Uses 1 Byte per pixel instead of 3. Testing uncapped speeds (esp32dev hub75 build) it ran about 50% faster on 32x32 and 64x32. Improved repeat detection that detects a majority of gliders and oscillators. Not 100% perfect on smaller grids that can produce long repeating patterns due to wrap, I kept mutation as a toggle option for these sizes (8x8 for example). Added blur slider to fade out dying cells.

Previous version had a max update speed of ~10 per second. I increased it to 42 like the old comment suggested.

Videos showing blur:

GoL.64x64.mp4
GoL.Blend.mp4

Patterns that are not detected:
8x8 has oscillators that repeat every 48 and 132 frames. Detection works on patterns <32 for 8x8
24x16 has a spaceship that repeats every 2448 frames. Detection works on patterns <196 for 24x16

Are 2D effects allowed to use 1D version of get/setPixelColor? I didn't include them in this PR but I did test them. They seemed to work fine and reduced code size a bit since in certain places I calculate x and y from an index. For example:

SEGMENT.setPixelColorXY(i % cols, i / cols, newColor);
SEGMENT.setPixelColor(i, newColor); 

Summary by CodeRabbit

  • New Features
    • Revamped 2D Game of Life effect with richer cell states, enabling detection of repeating patterns (oscillators, spaceships) and improved edge-aware behavior.
    • Smoother, more dynamic color and brightness transitions per cell for more vivid, evolving visuals.
    • Updated mode descriptor to clearly reflect new capabilities (e.g., blur, mutation) for easier selection and tuning.

RAM and speed optimizations. Better repeat detection. Mutation toggle. Blur option added.
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Walkthrough

Overhauls 2D Game of Life in FX.cpp: replaces ColorCount with public Cell bitfield, switches to a contiguous Cell array, removes CRC-based repetition detection, adds per-cell flags (edge/oscillator/spaceship), rewrites setup and evolution logic, and updates the mode’s metadata string.

Changes

Cohort / File(s) Summary
2D Game of Life overhaul
wled00/FX.cpp
- Replace ColorCount with public Cell bitfield (alive, faded, toggleStatus, edgeCell, oscillatorCheck, spaceshipCheck)
- Allocate/use contiguous Cell array; track maxIndex via cols×rows
- Remove prevLeds/CRC repetition detection and buffers
- New setup initializing cells, flags, and colors
- Reworked evolution loop using per-cell state machine and palette updates
- Update FX mode metadata string

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Game of Life Rework" accurately and succinctly summarizes the primary change in this pull request, which is a comprehensive overhaul of the Game of Life effect implementation. It is clear, concise, and directly reflects the main objective described in the PR summary without including extraneous details. This phrasing allows reviewers to immediately understand the scope of the changes.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
wled00/FX.cpp (1)

5289-5303: Early break both loops when neighbors > 3 (micro-optim).

break exits only the inner loop; the outer i-loop continues. Short‑circuit both loops to save work on dense patterns.

-    for (int i = 1; i >= -1; i--) for (int j = 1; j >= -1; j--) if (i || j) {
+    bool done = false;
+    for (int i = 1; i >= -1 && !done; i--) for (int j = 1; j >= -1; j--) if (i || j) {
         int nX = x + j, nY = y + i;
         if (cell.edgeCell) {
           nX = (nX + cols) % cols;
           nY = (nY + rows) % rows;
         }
         unsigned nIndex = nX + nY * cols;
         Cell& neighbor = cells[nIndex];
         if (neighbor.alive) {
-          if (++neighbors > 3) break;
+          if (++neighbors > 3) { done = true; break; }
           if (!neighbor.toggleStatus) { // Alive and not dying
             parentIdx[aliveParents++] = nIndex;
           }
         }
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91fdb58 and 65b9176.

📒 Files selected for processing (1)
  • wled00/FX.cpp (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.cpp

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use 2-space indentation for C++ source files (.cpp)

Files:

  • wled00/FX.cpp
🧬 Code graph analysis (1)
wled00/FX.cpp (3)
wled00/FX_fcn.cpp (2)
  • blur (1071-1097)
  • blur (1071-1071)
wled00/fcn_declare.h (3)
  • hw_random8 (433-433)
  • hw_random8 (434-434)
  • hw_random8 (435-435)
wled00/colors.cpp (1)
  • color_blend (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
🔇 Additional comments (1)
wled00/FX.cpp (1)

5355-5356: Metadata: surface fits behavior; confirm slider wiring.

Blur is mapped from custom1 and mutation from check3 in code; metadata declares "Blur" and "Mutation" accordingly. Looks consistent. Please confirm mobile UI ties custom1/check3 to these controls.

Comment on lines +5199 to +5201
typedef struct Cell {
uint8_t alive : 1, faded : 1, toggleStatus : 1, edgeCell: 1, oscillatorCheck : 1, spaceshipCheck : 1, unused : 2;
} Cell;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Enforce 1-byte Cell packing at compile time.

Bitfield packing is implementation-defined. Add a static_assert to guarantee 1 byte per cell across toolchains.

 typedef struct Cell {
     uint8_t alive : 1, faded : 1, toggleStatus : 1, edgeCell: 1, oscillatorCheck : 1, spaceshipCheck : 1, unused : 2; 
 } Cell;
+static_assert(sizeof(Cell) == 1, "Cell must be 1 byte to meet memory/perf goals");
🤖 Prompt for AI Agents
In wled00/FX.cpp around lines 5199 to 5201, the Cell bitfield layout is
implementation-defined and may not be 1 byte; add a compile-time check
immediately after the typedef: use a static_assert that sizeof(Cell) == 1 with a
clear message (e.g., "Cell must be 1 byte") so builds fail if packing differs
across toolchains; keep the assert close to the struct definition for clarity.

Comment on lines +5209 to +5212
if (!SEGENV.allocateData(SEGMENT.length() * sizeof(Cell))) return mode_static(); // allocation failed

Cell *cells = reinterpret_cast<Cell*> (SEGENV.data);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix allocation size to match iteration domain (cols*rows).

You iterate maxIndex = cols*rows but allocate SEGMENT.length() cells. In some 2D mappings these can diverge. Allocate exactly maxIndex to avoid OOB writes on atypical mappings.

Apply this diff:

-  if (!SEGENV.allocateData(SEGMENT.length() * sizeof(Cell))) return mode_static(); // allocation failed
+  if (!SEGENV.allocateData(maxIndex * sizeof(Cell))) return mode_static(); // allocation failed
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!SEGENV.allocateData(SEGMENT.length() * sizeof(Cell))) return mode_static(); // allocation failed
Cell *cells = reinterpret_cast<Cell*> (SEGENV.data);
if (!SEGENV.allocateData(maxIndex * sizeof(Cell))) return mode_static(); // allocation failed
Cell *cells = reinterpret_cast<Cell*>(SEGENV.data);
🤖 Prompt for AI Agents
In wled00/FX.cpp around lines 5209 to 5212, the code allocates SEGMENT.length()
Cell entries but the iteration domain uses maxIndex = cols * rows, which can
differ for 2D mappings and cause out-of-bounds writes; change the allocation to
request maxIndex * sizeof(Cell) (i.e., allocate exactly cols*rows Cells), keep
the existing null-check and fallback to mode_static() on failure, and then cast
SEGENV.data to Cell* as before.

Comment on lines +5223 to +5226
unsigned a = rows, b = cols;
while (b) { unsigned t = b; b = a % b; a = t; }
gliderLength = (cols * rows / a) << 2;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard gliderLength against overflow.

gliderLength = LCM(rows,cols)*4 stored in uint16_t can overflow on larger grids. Use 32-bit during calc and clamp/store to aux1.

-    gliderLength = (cols * rows / a) << 2;
+    uint32_t gl = ((uint32_t)cols * (uint32_t)rows / a) << 2;
+    gliderLength = (gl > UINT16_MAX) ? UINT16_MAX : (uint16_t)gl;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unsigned a = rows, b = cols;
while (b) { unsigned t = b; b = a % b; a = t; }
gliderLength = (cols * rows / a) << 2;
}
unsigned a = rows, b = cols;
while (b) { unsigned t = b; b = a % b; a = t; }
uint32_t gl = ((uint32_t)cols * (uint32_t)rows / a) << 2;
gliderLength = (gl > UINT16_MAX) ? UINT16_MAX : (uint16_t)gl;
}
🤖 Prompt for AI Agents
In wled00/FX.cpp around lines 5223 to 5226, the gliderLength calculation
(LCM(rows,cols)*4) can overflow uint16_t; compute using 32-bit temporaries: cast
rows and cols to uint32_t, compute gcd then lcm as (rows / gcd) * cols to avoid
intermediate overflow, multiply the 32-bit lcm by 4, clamp the result to the
maximum uint16_t value (0xFFFF) and then store the clamped value into aux1 (or
the uint16_t gliderLength), ensuring the assignment uses the clamped 16-bit
value.

@blazoncek
Copy link
Collaborator

Are 2D effects allowed to use 1D version of get/setPixelColor?

Yes. 2D is built on top of 1D functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants