Question

I'm trying to iterate through a matrix and check for the number of cells touching the current cell that have a value of 1. I'm getting an out of bounds exception and I'm not sure why.

for x in range(0,ROWS):
    for y in range(0,COLS):

        #get neighbors
        neighbors = []
        if x!=0 & y!=COLS:
            neighbors.append([x-1,y+1])
        if y!=COLS:
            neighbors.append([x,y+1])
        if x!=ROWS & y!=COLS:
            neighbors.append([x+1,y+1])
        if x!=0:
            neighbors.append([x-1,y])
        if x!=ROWS:
            neighbors.append([x+1,y])
        if x!=0 & y!=0:
            neighbors.append([x-1,y-1])
        if y!=0:
            neighbors.append([x,y-1])
        if x!=ROWS & y!=0:
            neighbors.append([x+1,y-1])

        #determine # of living neighbors
        alive = []
        for i in neighbors:
            if matrix[i[0]][i[1]] == 1:
                alive.append(i)

I'm getting the error

IndexError: list index out of range

at this line if matrix[i[0]][i[1]] == 1:

Why is this out of range and how should I fix it?

Was it helpful?

Solution

The problem is that you are using &. This is a bit-wise AND, not a logical AND. In Python, you just use and. For example:

if x!=0 and y!=COLS:
    neighbors.append([x-1,y+1])

However the real reason why using the bit-wise AND causes a problem is order of operations - it has a higher precedence!

>>> 1 != 2 & 3 != 3
True
>>> (1 != 2) & (3 != 3)
False
>>> 1 != (2 & 3) != 3
True

So even though your logic looks right, order of operations means that your code's actual behavior is drastically different than what you were expecting.

The other issue with your code is that you are checking if x and y are equal to ROWS and COLS, rather than if they are equal to ROWS-1 and COLS-1, which are the true boundary conditions.

OTHER TIPS

EDIT: I THINK I FOUND IT

Upon further inspection of your code, I found that you're using if x!=0 & y!=0. This is bit-wise AND not logical AND, so it's not going to give you the result you want. Use and instead of & and see if your problem goes away.

I would refactor this slightly to make it easier to read.

for loc_x in range(ROWS):
    for loc_y in range(COLS): # btw shouldn't ROWS/COLS be flipped?
                              # if your matrix isn't square this could be why
        x_values = [loc_x]
        if loc_x < ROWS: x_values.append(loc_x+1)
        if loc_x > 0: x_values.append(loc_x-1)
        y_values = [loc_y]
        if loc_y < COLS: y_values.append(loc_y+1)
        if loc_y > 0: y_values.append(loc_y-1)
        neighbors = [(x,y) for x in x_values for y in y_values if (x,y) != (loc_x,loc_y)]

        alive = [matrix[n[0]][n[1]] for n in neighbors if matrix[n[0]][n[1]]==1]

Try running this with your code and see if it doesn't solve the issue. If not, you may need to test further. For instance, wrap the alive definition in try/except tags that will give a better traceback.

try:
    alive = ...
except IndexError:
    print("Location: {},{}\nneighbors: {}\nROWS:{}\nCOLS:{}".format(x_loc,y_loc, neighbors,ROWS,COLS))
    raise

As an aside, I've solved this problem before by creating objects that held the linked information and going top-to-bottom left-to-right and having each check the field to its right and below it. E.g.:

class Field(object):
    def __init__(self,x,y,value):
        self.x = x
        self.y = y
        self.value = value
        self.neighbors = neighbors

class Matrix(list):
    def __init__(self,size):
        self.ROWS,self.COLS = map(int,size.lower().split("x"))
        for y in range(ROWS):
            self.append([Field(x,y,random.randint(0,1)) for x in range(COLS)])
        self.plot()
    def plot(self):
        for row in self:
            for col in row:
                try:
                    self[row][col].neighbors.append(self[row+1][col])
                    self[row+1][col].neighbors.append(self[row][col])
                except IndexError: pass
                try:
                    self[row][col].neighbors.append(self[row][col+1])
                    self[row][col+1].neighbors.append(self[row][col])
                except IndexError: pass

Of course this doesn't take care of diagonals. I'm pretty sure you can figure out how to manage those, though!!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top