How to loop through all neighbors of a cell in a board represented by 2-dimensional array?

2.1k views Asked by At

Building Game of Life here and working on a function to loop through all the neighbors of a cell and sum up the scores (each cell is 0 or 1). Board is represented by a 2-dimensional array. Normally a cell has 8 neighbours. The problem is that cells in the corner of the board have only 3 neighbors and cells on the side have 5. If I loop through the array using the code below, some neighbors return undefined in the array. I want to convert undefined to 0 and use that to sum up the scores, but i get the error:

Uncaught TypeError: Cannot read property '-1' of undefined

Thanks for your help!

var array = [
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1],
    [0,0,1,0,1,0,1,0,1,1]
]

for(var i=0; i < array.length; i++){
    for(var j=0; j < array[i].length; j++){

    var totalScore = 0;        
    var scores = [
        //i = row in loop
        //j = column nested loop  
        array[i-1][j-1],
        //upper left corner
        array[i-1][j],
        //top side
        array[i-1][j+1],
        //upper right corner
        array[i][j-1],
        //left side
        array[i][j+1],
        //right side
        array[i+1][j-1],
        //bottom left corner
        array[i+1][j],
        //bottom side
        array[i+1][j+1]
        //bottom right corner
        ];

        scores.forEach(function(item){

              var score = item;
              if(score === "undefined"){

                score = 0;

              }

              totalScore += score;

            })

            console.log(totalScore);    

    }

}
3

There are 3 answers

0
MBo On

@Matt Timmermans proposition solves a lot of problems. If you want to keep existing structure, consider next approach:

Form code describing cell position in the matrix (I assume boolean is evaluated as 0/1):

dxdy_code = (y==0) + (x==0)<<1 + (y==height-1)<<2 + (x==width-1)<<3;

Build array containing all possible combinations (size 16, binary 0b0000..0b1111) of neighbour shifts:

var dxdy = [
[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]],  //all
[[0,-1],[0,1],[1,-1],[1,0],[1,1]],  //y=0
[[-1,0],[-1,1],[0,1],[1,0],[1,1]],  //x=0
[[0,1],[1,0],[1,1]],  //y=0,x=0
[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1]],  //y=max
[[0,-1],[0,1]],  //one row
[[-1,0],[-1,1],[0,1]],  //y=max,x=0
..
[]  //one row, one column 
]

For every cell calculate code and use corresponding array of shifts

for(var i=0; i < array.length; i++){
    for(var j=0; j < array[i].length; j++){
    var totalScore = 0;
    code =  dxdy_code(i, j);
    sh = dxdy[code];
    for (var k=0; k < sh.length; k++){
        totalscore += array[i+sh[k][0]][j+sh[k][1]];
    }
0
Ouroborus On

Just a lot of bounds checking:

var scores = [
  i > 0 && j > 0 ? array[i-1][j-1] : 0,
  i > 0 ? array[i-1][j] : 0,
  i > 0 && j < array[i].length-1 ? array[i-1][j+1] : 0,
  j > 0 ? array[i][j-1] : 0,
  j < array[i].length-1 ? array[i][j+1] : 0,
  i < array.length-1 && j > 0 ? array[i+1][j-1] : 0,
  i < array.length-1 ? array[i+1][j] : 0,
  i < array.length-1 && j < array[i].legnth-1 ? array[i+1][j+1] : 0
];

You could also create a function to act as an intermediary and access the array through that:

function getCell(x,y) {
  if(y < 0 || y >= array.length || x < 0 || x >= array[0].length) {
    return 0;
  }
  return array[y][x];
}
0
chemook78 On

Thanks everyone. With your help I solved it as below:

var array = [

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1],

  [0, 0, 1, 0, 1, 0, 1, 0, 1, 1]

]

for (var i = 0; i < array.length; i++) {

  for (var j = 0; j < array[i].length; j++) {

    var scores = [
      i > 0 && j > 0 ? array[i - 1][j - 1] : 0,
      i > 0 ? array[i - 1][j] : 0,
      i > 0 && j < array[i].length - 1 ? array[i - 1][j + 1] : 0,
      j > 0 ? array[i][j - 1] : 0,
      j < array[i].length - 1 ? array[i][j + 1] : 0,
      i < array.length - 1 && j > 0 ? array[i + 1][j - 1] : 0,
      i < array.length - 1 ? array[i + 1][j] : 0,
      i < array.length - 1 && j < array[i].length - 1 ? array[i + 1][j + 1] : 0
    ]

    var totalScore = scores.reduce(function(total, currentValue) {

      return total += currentValue;

    })

    console.log(totalScore);

  }

}

Will try the other suggestions when refactoring my code!