Reputation: 677
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?
Upvotes: 0
Views: 1015
Reputation: 54173
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!!
Upvotes: 3
Reputation: 7146
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.
Upvotes: 3