Error in R4 Game of Life Example - Fixed

If you look at the Game of Life example in the Examples menu for the R4 WIFI you will find that it appears to work. But further inspection shows that it does not if the pattern extends beyond place 8 in the Y direction.
In fact there is a comment in the code that says:-
// grid dimensions should not be larger than 8x8
This is because the function that counts adjacent cells uses fixed "magic" numbers of 8. Replacing these with the #defined values cure the problem.
I have also changed the Glider example so that it crashes into a 2 step repeating pattern. Which was something I was trying to do when I discovered the error.

/*
  Game Of Life

  The Game of Life, also known simply as Life, is a cellular automaton devised 
  by the British mathematician John Horton Conway in 1970. It is a zero-player game, 
  meaning that its evolution is determined by its initial state, requiring no further 
  input.

  Example developed starting from Toby Oxborrow's sketch
  https://github.com/tobyoxborrow/gameoflife-arduino/blob/master/GameOfLife.ino

  See the full documentation here:
  https://docs.arduino.cc/tutorials/uno-r4-wifi/led-matrix
*/

#include "Arduino_LED_Matrix.h" // Include the LED_Matrix library
// grid dimensions should not be larger than 8x8 - Now fixed by Grumpy_Mike
#define MAX_Y 8
#define MAX_X 12

// time to wait between turns
#define TURN_DELAY 200

// how many turns per game before starting a new game
// you can also use the reset button on the board
#define TURNS_MAX 60

// number of patterns in predefined list
#define MAX_PATTERNS 4

// how many turns to wait if there are no changes before starting a new game
#define NO_CHANGES_RESET 4

int turns = 0;       // counter for turns
int noChanges = 0;  // counter for turns without changes

// game state. 0 is dead cell, 1 is live cell
uint8_t grid[MAX_Y][MAX_X] = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
};

int currentPattern = 0;

String patternNames[] = {
  "Glider Crash",
  "Light-weight spaceship",
  "R-Pentomino",
  "Diehard"
};

// custom starting grid patterns
boolean cGrids[][MAX_Y][MAX_X] = {
    { /* Glider Crash */
        {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    },
    { /* Light-weight spaceship */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0},
        {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    },
    { /* R-Pentomino */
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    },
    { /* Die hard */
        {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
        {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    }
};


ArduinoLEDMatrix matrix;

void setup() {
  Serial.begin(9600);
  delay(1000);

  Serial.println("Conway's game of life on Arduino LED Matrix");
  matrix.begin();
  
  resetGrid();
  displayGrid();

}

void loop() {
  delay(TURN_DELAY);

  playGoL();

  turns++;

  // reset the grid if no changes have occured recently
  // for when the game enters a static stable state
  if (noChanges > NO_CHANGES_RESET) {
    resetGrid();

  }
  // reset the grid if the loop has been running a long time
  // for when the game cycles between a few stable states
  if (turns > TURNS_MAX) {
    resetGrid();
  }

  displayGrid();
}

// play game of life
void playGoL() {
  /*
    1. Any live cell with fewer than two neighbours dies, as if by loneliness.
    2. Any live cell with more than three neighbours dies, as if by
    overcrowding.
    3. Any live cell with two or three neighbours lives, unchanged, to the next
    generation.
    4. Any dead cell with exactly three neighbours comes to life.
    */

  boolean newGrid[MAX_Y][MAX_X] = {
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  };

  for (int y = 0; y < MAX_Y; y++) {
    for (int x = 0; x < MAX_X; x++) {
      int neighboughs = countNeighbours(y, x);
      if (grid[y][x] == 1) {
        if ((neighboughs == 2) || (neighboughs == 3)) {
          newGrid[y][x] = 1;
        } else {
          newGrid[y][x] = 0;
        }
      } else {
        if (neighboughs == 3) {
          newGrid[y][x] = 1;
        } else {
          newGrid[y][x] = 0;
        }
      }
    }
  }

  // update the current grid from the new grid and count how many changes
  // occured
  int changes = 0;
  for (int y = 0; y < MAX_Y; y++) {
    for (int x = 0; x < MAX_X; x++) {
      if (newGrid[y][x] != grid[y][x]) {
        changes++;
      }
      grid[y][x] = newGrid[y][x];
    }
  }

  // update global counter when no changes occured
  if (changes == 0) {
    noChanges++;
  }
}

// count the number of neighbour live cells for a given cell
int countNeighbours(int y, int x) {
  int count = 0;

  // -- Row above us ---
  if (y > 0) {
    // above left
    if (x > 0) {
      count += grid[y - 1][x - 1];
    }
    // above
    count += grid[y - 1][x];
    // above right
    if ((x + 1) < MAX_X) {
      count += grid[y - 1][x + 1];
    }
  }

  // -- Same row -------
  // left
  if (x > 0) {
    count += grid[y][x - 1];
  }
  // right
  if ((x + 1) < MAX_X) {
    count += grid[y][x + 1];
  }

  // -- Row below us ---
  if ((y + 1) < MAX_Y) {
    // below left
    if (x > 0) {
      count += grid[y + 1][x - 1];
    }
    // below
    count += grid[y + 1][x];
    // below right
    if ((x + 1) < MAX_X) {
      count += grid[y + 1][x + 1];
    }
  }

  return count;
}

// reset the grid
void resetGrid() {
  Serial.print("Current pattern: ");
  Serial.println(patternNames[currentPattern]);
  noChanges = 0;
  turns = 0;

  for (int y = 0; y < MAX_Y; y++) {
    for (int x = 0; x < MAX_X; x++) {
      grid[y][x] = cGrids[currentPattern][y][x];
    }
  }
  currentPattern++;
  if(currentPattern >= MAX_PATTERNS){
    currentPattern = 0;
  }
}

// display the current grid to the LED matrix
void displayGrid() {
  matrix.renderBitmap(grid, 8, 12);
}

I will leave the misspelling of neighbors as neighbours for others to correct if they want to, it will not affect the running of the code.

Maybe some one on the Arduino team (maybe @ptillisch ) could arrange for this correction to make it into the official examples shipped with the R4 WIFI board.

3 Likes

Looks like fun, will try it out!

But I think there was a cut and paste problem with the code.

#include "Arduino_LED_Matrix.h" // Include the LED_Matrix library

// grid dimensions should not be larger than 8x8 - Now f

1. List item

ixed by Grumpy_Mike
#define MAX_Y 8
#define MAX_X 12

Guessing that maybe it is supposed to be:

#include "Arduino_LED_Matrix.h" // Include the LED_Matrix library

// grid dimensions should not be larger than 8x8 - Now fixed by Grumpy_Mike
#define MAX_Y 8
#define MAX_X 12

That is what I am running on it now...

1 Like

Yes you are right. Not sure how that happened. Anyway fixed in the first post. Thanks.

While I was at it I decided to turn the Life grid patterns into some musical notes, so I set up a crash between a glider and a blinker.
The video is here:-

And a link to the code and explanation of what is going on and things to try with it are in the notes attached to the video.

Or without the YouTube tracking of your clicks use:-