Trouble getting Conway's Game of Life working right

I'm trying to get Conway's Game of Life (Conway's Game of Life - Wikipedia) working on an Arduino over the serial monitor. Still lifes do what they should (at least blocks and loaves), but when I try to create a glider, it gives me this shape:

░ ░ ░ ░
░ ░ █ ░
░ █ █ ░
░ █ ░ ░
░ ░ ░ ░

This should turn into a beehive, but it remains this way forever. I'm not sure what part of my code is wrong:

bool matrix [10][10] = {
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
  {0, 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, 0, 0, 0}
};

int currentX = 0;
int currentY = 0;
int liveNeighborCount = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  for (int i = 0; i < 99; i++) {
    if (matrix[currentX][currentY] == 0) {
      Serial.print("░ ");
    }
    else {
      Serial.print("█ ");
    }
    currentX++;
    if (currentX == 9) {
      currentX = 0;
      currentY++;
      Serial.print("\n");
    }
  }
  Serial.print("\n");

  for (int i = 0; i < 99; i++) {
    liveNeighborCount = 0;
    if (matrix[currentX - 1][currentY - 1] == 1 && currentX != 0 && currentY != 0) { //check neighbor at the top left of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX][currentY - 1] == 1 && currentY != 0) { //check neighbor directly above current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY - 1] == 1 && currentX != 0 && currentY != 9) { //check neighbor at the top right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY] == 1 && currentX != 0) { //check neighbor directly right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY + 1] == 1 && currentX != 9 && currentY != 9) { //check neighbor at the bottom right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX][currentY + 1] == 1 && currentY != 9) { //check neighbor directly below current cell
      liveNeighborCount++;
    }
    if (matrix[currentX - 1][currentY + 1] == 1 && currentX != 0  && currentY != 9) { //check neighbor at the bottom left of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX - 1][currentY] == 1 && currentX != 0) {
      liveNeighborCount++;
    }

    setCells();
    currentX++;
    if (currentX == 9) {
      currentX = 0;
      currentY++;
    }
  }

  currentX = 0;
  currentY = 0;


  delay(1000);
}

void setCells() {
  if (liveNeighborCount < 2 && matrix[currentX][currentY] == 1) {
    matrix[currentX][currentY] = 0;
    return;
  }
  else if (liveNeighborCount == 2 || liveNeighborCount == 3) {
    if (matrix[currentX][currentY] == 1) {
      matrix[currentX][currentY] = 1;
      return;
    }
    return;
  }
  else if (liveNeighborCount > 3 && matrix[currentX][currentY] == 1) {
    matrix[currentX][currentY] = 0;
    return;
  }
  else if (liveNeighborCount == 3 && matrix[currentX][currentY] == 0) {
    matrix[currentX][currentY] = 1;
    return;
  }
}

If you feel like trying to run it yourself, it should work on any Arduino compatible with a serial connection.

At the start of your loop() you display the contents of the array matrix.
After you have displayed it the variable currentY will have the value 11.
The remaining code then tries to calculate liveNeighborCount but the index currentY is outside the bounds of the matrix.

ardly:
At the start of your loop() you display the contents of the array matrix.
After you have displayed it the variable currentY will have the value 11.
The remaining code then tries to calculate liveNeighborCount but the index currentY is outside the bounds of the matrix.

Indeed it is! Forgive my ignorance, but how is that happening and how could I prevent it?

The main problem is that you are modifying matrix with the next value of a cell before you have finished dealing with all the other cells.
e.g. if cell matrix[i][j] is zero but it has exactly 3 live cells below it, you set it to one. Then you will check the cells below it but they will now have an extra neighbour immediately above that shouldn't be there yet.
Easiest way around it is to have two matrices. The "current" one and the "next" one. Process the "current" one but put the results into the "next" one. Once you're done, copy "next" into "current" and repeat.

Pete

el_supremo:
The main problem is that you are modifying matrix with the next value of a cell before you have finished dealing with all the other cells.
e.g. if cell matrix[i][j] is zero but it has exactly 3 live cells below it, you set it to one. Then you will check the cells below it but they will now have an extra neighbour immediately above that shouldn't be there yet.
Easiest way around it is to have two matrices. The "current" one and the "next" one. Process the "current" one but put the results into the "next" one. Once you're done, copy "next" into "current" and repeat.

Pete

bool matrix [10][10] = {
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
  {0, 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, 0, 0, 0}
};

bool nextMatrix [10][10] = {
  {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, 0, 0, 0, 0}
};

int currentX = 0;
int currentY = 0;
int liveNeighborCount = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  for (int i = 0; i < 100; i++) {
    if (matrix[currentX][currentY] == 0) {
      Serial.print("░ ");
    }
    else {
      Serial.print("█ ");
    }
    currentX++;
    if (currentX == 10) {
      currentX = 0;
      currentY++;
      Serial.print("\n");
    }
  }
  Serial.print("\n");

  for (int i = 0; i < 100; i++) {
    liveNeighborCount = 0;
    if (matrix[currentX - 1][currentY - 1] == 1 && currentX != 0 && currentY != 0) { //check neighbor at the top left of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX][currentY - 1] == 1 && currentY != 0) { //check neighbor directly above current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY - 1] == 1 && currentX != 0 && currentY != 9) { //check neighbor at the top right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY] == 1 && currentX != 0) { //check neighbor directly right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX + 1][currentY + 1] == 1 && currentX != 9 && currentY != 9) { //check neighbor at the bottom right of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX][currentY + 1] == 1 && currentY != 9) { //check neighbor directly below current cell
      liveNeighborCount++;
    }
    if (matrix[currentX - 1][currentY + 1] == 1 && currentX != 0  && currentY != 9) { //check neighbor at the bottom left of current cell
      liveNeighborCount++;
    }
    if (matrix[currentX - 1][currentY] == 1 && currentX != 0) {
      liveNeighborCount++;
    }

    setCells();
    currentX++;
    if (currentX == 10) {
      currentX = 0;
      currentY++;
    }
  }

  currentX = 0;
  currentY = 0;

  for (int i = 0; i < 100; i++) {
    //delay(100);
    //Serial.println(currentX);
    //Serial.println(currentY);
    //delay(100);
    matrix[currentX][currentY] = nextMatrix[currentX][currentY];
    currentX++;
    if (currentX > 9) {
      currentX = 0;
      currentY++;
    }
  }
  currentX = 0;
  currentY = 0;
  delay(1000);
}

void setCells() {
  if (liveNeighborCount < 2 && matrix[currentX][currentY] == 1) {
    nextMatrix[currentX][currentY] = 0;
    return;
  }
  else if (liveNeighborCount == 2 || liveNeighborCount == 3) {
    if (matrix[currentX][currentY] == 1) {
      nextMatrix[currentX][currentY] = 1;
      return;
    }
    return;
  }
  else if (liveNeighborCount > 3 && matrix[currentX][currentY] == 1) {
    nextMatrix[currentX][currentY] = 0;
    return;
  }
  else if (liveNeighborCount == 3 && matrix[currentX][currentY] == 0) {
    nextMatrix[currentX][currentY] = 1;
    return;
  }
}

Here's what I have now. I'm fairly sure I've done it wrong since it doesn't work, but I don't know what needs changing.

Huge thanks to everyone involved for walking me through this!

I would rewrite it to use nested for loops instead of having currentX and currentY.
Like this:

 for(uint8_t y = 0; y < 10; y++) {
    for(uint8_t x = 0; x < 10; x++) {
      // now refer an element as matrix[y][x]
    }
  }

But, I would also handle the special cases separately - do the four corners individually (because they each only have 3 neighbours), handle the remaining elements of the top and bottom rows, and handle the remainder of the leftmost and rightmost columns. Then, all the interior elements in the matrix can be handled in a loop like this:

 for(uint8_t y = 1; y < 9; y++) {
    for(uint8_t x = 1; x < 9; x++) {
      // every element matrix[y][x] has 8 neighbours
    }
  }

This gets rid of testing for special cases of currentX and currentY - I think you aren't handling the edges correctly.

Pete

el_supremo:
But, I would also handle the special cases separately - do the four corners individually (because they each only have 3 neighbours), handle the remaining elements of the top and bottom rows, and handle the remainder of the leftmost and rightmost columns.

Just to make sure I'm understanding right, I should individually check each corner and then have something along the lines of this?

for (int x = 1; x < 9; x++) {
  //get value of matrix[x][0] for top row excluding corners
}

Almost. The top row is as shown below and you could do the bottom row in the same loop:

for (int x = 1; x < 9; x++) {
  //get value of matrix[0][x] for top row excluding corners

  //get value of matrix[9][x] for bottom row excluding corners
}

and a similar loop for the left and right column edges.

Pete

snuu:
Indeed it is! Forgive my ignorance, but how is that happening and how could I prevent it?

Sorry for the delay, the website seemed to be down for a while yesterday.

You still have not dealt with the above bug, a quick and dirty way to kill it is to just put the statement
currentY = 0;
after you have printed the matrix contents and before the loop that counts the live neighbours.

Really though you need to restructure your code because you are still making mistakes. Presently, for example, currentY takes values between 10...19 which is outside he bounds of the array matrix.

Firstly don't use numeric literals such as 9, 10, and 100 in your code, they are error prone and make changes difficult.
Instead define a couple of constants;
const int matrixX=10;
const int matrixY=10;
Use these constants throughout your sketch. You will avoid errors and you can easily change your matrix size from 10x10 to say 12x12 or even from a square to a rectangular matrix.

Secondly put each task into a separate function e.g.
loop()
{
printMatrix();
countNeighbours();
}

Thirdly within the functions process the cells using nested 'for' statements, an outer 'for' to process each row in your matrix and an inner 'for' for each column within the row. This makes the program structure much more closely reflect what you are trying to do and so makes errors more obvious.

Fourthly and finally, put some strategic print statements in your sketch for debugging so that you can see what values currentX and currentY are actually taking. These are not what you are expecting at the moment :slight_smile:

@el_supremo is correct that you should be using two matrices or, I think better, make the current matrix three dimensional.