(Game of Life) How to loop through outer layer of

2020-05-06 01:23发布

问题:

Each point in the matrix represents a cell, living or dead. I have to count how many ALIVE neighbors each cell has. I have a function that does the job, but it checks for cells outside its boundary.I don't know how I can simultaneously check for neighbors and keep track of edges without doing a massive amount of if-else statements.

void Neighbours(int rows, 
                int cols, cell world[rows][cols], 
                int neighbors[rows][cols]) {

//Loop through each cell in the matrix. 
for(int rCell = 0; rCell < rows; rCell++){
  for(int cCell = 0; cCell < cols; cCell++) {
    //Reset neighbor count for each cell.
    neighbors[rCell][cCell] = 0;
    //Check cell status in cell's vicinity of each cell. 
    for(int surroundR = -1; surroundR <= 1; surroundR++){
      for(int surroundC = -1; surroundC <= 1; surroundC ++) {
        //CONDITIONS
        //1. If the cell is alive,
        //2. if the cell is not itself,
        //3. if it does exist within the boundaries of the matrix.
        if(field[rCell - surroundR][cCell - surroundC].status == ALIVE) {
          if(!(surroundR  == 0 && surroundC  == 0) && (rCell-surroundR < rows) && (cCell-surroundC < cols) && (cCell-surroundC  >= 0) && (rCell-surroundR  >= 0)) {
            neighbors[rCell][cCell] += 1;
          }
        }
      }
    }
  } 
}
}

回答1:

The easiest way to handle this is to add two dummy rows and columns: a row above your matrix, a row below your matrix, a column to the left of your matrix, and a column to the right of your matrix. You set them to DEAD once and for all, and run your loops only on the cells in the original matrix.



回答2:

Always when you have this kind of check - before indexing into array check whether the indices are within the size of the array on which you are about to index. You have to do that first before indexing into array. Earlier you had undefined behavior due to accessing array index out of bound.

if( (rCell - surroundR) <= rows-1 && (cCell - surroundC)<= cols-1 && (rCell - surroundR)>=0 && (cCell - surroundC)>=0 ) {
   /* then do rest of work */

}


回答3:

You can easily sell some memory to save time by using an array containing one row on each side with only dead elements, and do the processing only in the inside. The algorithm becomes simpler with less risk of errors, but but whole code must be changed to add 2 lines and 2 rows:

void Neighbours(int rows, 
                int cols, cell world[rows][cols], 
                int neighbors[rows][cols]) {

//Loop through each cell in the matrix. 
for(int rCell = 1; rCell < rows-1; rCell++){   // limit to the interior
  for(int cCell = 1; cCell < cols-1; cCell++) {
     // remaining part of the loops is unchanged


回答4:

One method is to pad the array with rows and columns of dead cells at the edges of the array.

Another method is to write separate pieces of code:

  • One loop processes interior cells (omitting the edge rows and columns) of the array, checking all neighbors.

  • One loop processes the top row, checking only actual neighbors of cells of the top row. Only cells interior to the row are processed, omitting the corners.

  • Similarly, there is one loop for the bottom row, one for the left column, and one for the right column.

  • The four corners are checked separately.

Although this is more code, it executes fewer checks than one loop checking for each cell. Note that that the loops for the top row and the bottom row may be combined (because they share the column coordinate), and the loops for the left edge and the right edge may be combined.

Additional reductions of the computational load are possible. For example, the counts of living cells in each 3*1 strip in the main array can be cached (in an auxiliary array or simply in variables in a carefully crafted loop) to be reused. Each such strip in the interior of the main array is used three times in counting the 3*3 squares around various cells, so caching their counts can eliminate some repeated work.

(Regarding padding: To use proper C code, you need two columns, one on the left and one on the right. However, if your C implementation supported accessing subarray elements out of bounds as long as the addressed element is in the complete array, or you manually map two dimensions into a one-dimensional array, then one padding column would suffice, since it would be both to the left of the left edge of the array proper and to the right of the right edge.)